]>
git.proxmox.com Git - pve-manager.git/blob - PVE/API2/Ceph.pm
1 package PVE
::API2
::CephOSD
;
9 use PVE
::Tools
qw(extract_param run_command file_get_contents file_read_firstline dir_glob_regex dir_glob_foreach);
10 use PVE
::Exception
qw(raise raise_param_exc);
12 use PVE
::Cluster
qw(cfs_lock_file cfs_read_file cfs_write_file);
13 use PVE
::AccessControl
;
15 use PVE
::API2
::Storage
::Config
;
17 use PVE
::RPCEnvironment
;
18 use PVE
::JSONSchema
qw(get_standard_option);
23 use base
qw(PVE::RESTHandler);
25 use Data
::Dumper
; # fixme: remove
27 my $get_osd_status = sub {
28 my ($rados, $osdid) = @_;
30 my $stat = $rados->mon_command({ prefix
=> 'osd dump' });
32 my $osdlist = $stat->{osds
} || [];
34 my $flags = $stat->{flags
} || undef;
37 foreach my $d (@$osdlist) {
38 $osdstat->{$d->{osd
}} = $d if defined($d->{osd
});
40 if (defined($osdid)) {
41 die "no such OSD '$osdid'\n" if !$osdstat->{$osdid};
42 return $osdstat->{$osdid};
45 return wantarray?
($osdstat, $flags):$osdstat;
48 my $get_osd_usage = sub {
51 my $osdlist = $rados->mon_command({ prefix
=> 'pg dump',
52 dumpcontents
=> [ 'osds' ]}) || [];
55 foreach my $d (@$osdlist) {
56 $osdstat->{$d->{osd
}} = $d if defined($d->{osd
});
62 __PACKAGE__-
>register_method ({
66 description
=> "Get Ceph osd list/tree.",
70 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
73 additionalProperties
=> 0,
75 node
=> get_standard_option
('pve-node'),
78 # fixme: return a list instead of extjs tree format ?
85 PVE
::CephTools
::check_ceph_inited
();
87 my $rados = PVE
::RADOS-
>new();
88 my $res = $rados->mon_command({ prefix
=> 'osd tree' });
90 die "no tree nodes found\n" if !($res && $res->{nodes
});
92 my ($osdhash, $flags) = &$get_osd_status($rados);
94 my $usagehash = &$get_osd_usage($rados);
96 my $osdmetadata_tmp = $rados->mon_command({ prefix
=> 'osd metadata' });
99 foreach my $osd (@$osdmetadata_tmp) {
100 $osdmetadata->{$osd->{id
}} = $osd;
105 foreach my $e (@{$res->{nodes
}}) {
106 $nodes->{$e->{id
}} = $e;
114 foreach my $opt (qw(status crush_weight reweight device_class)) {
115 $new->{$opt} = $e->{$opt} if defined($e->{$opt});
118 if (my $stat = $osdhash->{$e->{id
}}) {
119 $new->{in} = $stat->{in} if defined($stat->{in});
122 if (my $stat = $usagehash->{$e->{id
}}) {
123 $new->{total_space
} = ($stat->{kb
} || 1) * 1024;
124 $new->{bytes_used
} = ($stat->{kb_used
} || 0) * 1024;
125 $new->{percent_used
} = ($new->{bytes_used
}*100)/$new->{total_space
};
126 if (my $d = $stat->{perf_stat
}) {
127 $new->{commit_latency_ms
} = $d->{commit_latency_ms
};
128 $new->{apply_latency_ms
} = $d->{apply_latency_ms
};
132 my $osdmd = $osdmetadata->{$e->{id
}};
133 if ($e->{type
} eq 'osd' && $osdmd) {
134 if ($osdmd->{bluefs
}) {
135 $new->{osdtype
} = 'bluestore';
136 $new->{blfsdev
} = $osdmd->{bluestore_bdev_dev_node
};
137 $new->{dbdev
} = $osdmd->{bluefs_db_dev_node
};
138 $new->{waldev
} = $osdmd->{bluefs_wal_dev_node
};
140 $new->{osdtype
} = 'filestore';
144 $newnodes->{$e->{id
}} = $new;
147 foreach my $e (@{$res->{nodes
}}) {
148 my $new = $newnodes->{$e->{id
}};
149 if ($e->{children
} && scalar(@{$e->{children
}})) {
150 $new->{children
} = [];
152 foreach my $cid (@{$e->{children
}}) {
153 $nodes->{$cid}->{parent
} = $e->{id
};
154 if ($nodes->{$cid}->{type
} eq 'osd' &&
155 $e->{type
} eq 'host') {
156 $newnodes->{$cid}->{host
} = $e->{name
};
158 push @{$new->{children
}}, $newnodes->{$cid};
161 $new->{leaf
} = ($e->{id
} >= 0) ?
1 : 0;
166 foreach my $e (@{$res->{nodes
}}) {
167 if (!$nodes->{$e->{id
}}->{parent
}) {
168 push @$roots, $newnodes->{$e->{id
}};
172 die "no root node\n" if !@$roots;
174 my $data = { root
=> { leaf
=> 0, children
=> $roots } };
176 # we want this for the noout flag
177 $data->{flags
} = $flags if $flags;
182 __PACKAGE__-
>register_method ({
186 description
=> "Create OSD",
190 additionalProperties
=> 0,
192 node
=> get_standard_option
('pve-node'),
194 description
=> "Block device name.",
198 description
=> "Block device name for journal (filestore) or block.db (bluestore).",
203 description
=> "Block device name for block.wal (bluestore only).",
208 description
=> "File system type (filestore only).",
210 enum
=> ['xfs', 'ext4'],
215 description
=> "Use bluestore instead of filestore. This is the default.",
222 returns
=> { type
=> 'string' },
226 my $rpcenv = PVE
::RPCEnvironment
::get
();
228 my $authuser = $rpcenv->get_user();
230 raise_param_exc
({ 'bluestore' => "conflicts with parameter 'fstype'" })
231 if (defined($param->{fstype
}) && defined($param->{bluestore
}) && $param->{bluestore
});
233 PVE
::CephTools
::check_ceph_inited
();
235 PVE
::CephTools
::setup_pve_symlinks
();
237 PVE
::CephTools
::check_ceph_installed
('ceph_osd');
239 my $bluestore = $param->{bluestore
} // 1;
244 if ($param->{journal_dev
} && ($param->{journal_dev
} ne $param->{dev
})) {
245 $journal_dev = PVE
::Diskmanage
::verify_blockdev_path
($param->{journal_dev
});
248 if ($param->{wal_dev
} &&
249 ($param->{wal_dev
} ne $param->{dev
}) &&
250 (!$param->{journal_dev
} || $param->{wal_dev
} ne $param->{journal_dev
})) {
251 raise_param_exc
({ 'wal_dev' => "can only be set with paramater 'bluestore'"})
253 $wal_dev = PVE
::Diskmanage
::verify_blockdev_path
($param->{wal_dev
});
256 $param->{dev
} = PVE
::Diskmanage
::verify_blockdev_path
($param->{dev
});
258 my $devname = $param->{dev
};
259 $devname =~ s
|/dev/||;
261 my $disklist = PVE
::Diskmanage
::get_disks
($devname, 1);
263 my $diskinfo = $disklist->{$devname};
264 die "unable to get device info for '$devname'\n"
267 die "device '$param->{dev}' is in use\n"
268 if $diskinfo->{used
};
270 my $devpath = $diskinfo->{devpath
};
271 my $rados = PVE
::RADOS-
>new();
272 my $monstat = $rados->mon_command({ prefix
=> 'mon_status' });
273 die "unable to get fsid\n" if !$monstat->{monmap
} || !$monstat->{monmap
}->{fsid
};
275 my $fsid = $monstat->{monmap
}->{fsid
};
276 $fsid = $1 if $fsid =~ m/^([0-9a-f\-]+)$/;
278 my $ceph_bootstrap_osd_keyring = PVE
::CephTools
::get_config
('ceph_bootstrap_osd_keyring');
280 if (! -f
$ceph_bootstrap_osd_keyring) {
281 my $bindata = $rados->mon_command({ prefix
=> 'auth get', entity
=> 'client.bootstrap-osd', format
=> 'plain' });
282 PVE
::Tools
::file_set_contents
($ceph_bootstrap_osd_keyring, $bindata);
288 my $fstype = $param->{fstype
} || 'xfs';
291 my $ccname = PVE
::CephTools
::get_config
('ccname');
293 my $cmd = ['ceph-disk', 'prepare', '--zap-disk',
294 '--cluster', $ccname, '--cluster-uuid', $fsid ];
297 print "create OSD on $devpath (bluestore)\n";
298 push @$cmd, '--bluestore';
301 print "using device '$journal_dev' for block.db\n";
302 push @$cmd, '--block.db', $journal_dev;
306 print "using device '$wal_dev' for block.wal\n";
307 push @$cmd, '--block.wal', $wal_dev;
310 push @$cmd, $devpath;
312 print "create OSD on $devpath ($fstype)\n";
313 push @$cmd, '--filestore', '--fs-type', $fstype;
315 print "using device '$journal_dev' for journal\n";
316 push @$cmd, '--journal-dev', $devpath, $journal_dev;
318 push @$cmd, $devpath;
326 return $rpcenv->fork_worker('cephcreateosd', $devname, $authuser, $worker);
329 __PACKAGE__-
>register_method ({
330 name
=> 'destroyosd',
333 description
=> "Destroy OSD",
337 additionalProperties
=> 0,
339 node
=> get_standard_option
('pve-node'),
341 description
=> 'OSD ID',
345 description
=> "If set, we remove partition table entries.",
352 returns
=> { type
=> 'string' },
356 my $rpcenv = PVE
::RPCEnvironment
::get
();
358 my $authuser = $rpcenv->get_user();
360 PVE
::CephTools
::check_ceph_inited
();
362 my $osdid = $param->{osdid
};
364 my $rados = PVE
::RADOS-
>new();
365 my $osdstat = &$get_osd_status($rados, $osdid);
367 die "osd is in use (in == 1)\n" if $osdstat->{in};
368 #&$run_ceph_cmd(['osd', 'out', $osdid]);
370 die "osd is still runnung (up == 1)\n" if $osdstat->{up
};
372 my $osdsection = "osd.$osdid";
377 # reopen with longer timeout
378 $rados = PVE
::RADOS-
>new(timeout
=> PVE
::CephTools
::get_config
('long_rados_timeout'));
380 print "destroy OSD $osdsection\n";
383 PVE
::CephTools
::ceph_service_cmd
('stop', $osdsection);
384 PVE
::CephTools
::ceph_service_cmd
('disable', $osdsection);
388 print "Remove $osdsection from the CRUSH map\n";
389 $rados->mon_command({ prefix
=> "osd crush remove", name
=> $osdsection, format
=> 'plain' });
391 print "Remove the $osdsection authentication key.\n";
392 $rados->mon_command({ prefix
=> "auth del", entity
=> $osdsection, format
=> 'plain' });
394 print "Remove OSD $osdsection\n";
395 $rados->mon_command({ prefix
=> "osd rm", ids
=> [ $osdsection ], format
=> 'plain' });
397 # try to unmount from standard mount point
398 my $mountpoint = "/var/lib/ceph/osd/ceph-$osdid";
400 my $disks_to_wipe = {};
401 my $remove_partition = sub {
404 return if !$part || (! -b
$part );
405 my $partnum = PVE
::Diskmanage
::get_partnum
($part);
406 my $devpath = PVE
::Diskmanage
::get_blockdev
($part);
408 print "remove partition $part (disk '${devpath}', partnum $partnum)\n";
409 eval { run_command
(['/sbin/sgdisk', '-d', $partnum, "${devpath}"]); };
412 $disks_to_wipe->{$devpath} = 1;
415 my $partitions_to_remove = [];
417 if ($param->{cleanup
}) {
418 if (my $fd = IO
::File-
>new("/proc/mounts", "r")) {
419 while (defined(my $line = <$fd>)) {
420 my ($dev, $path, $fstype) = split(/\s+/, $line);
421 next if !($dev && $path && $fstype);
422 next if $dev !~ m
|^/dev/|;
423 if ($path eq $mountpoint) {
424 my $data_part = abs_path
($dev);
425 push @$partitions_to_remove, $data_part;
432 foreach my $path (qw(journal block block.db block.wal)) {
433 my $part = abs_path
("$mountpoint/$path");
435 push @$partitions_to_remove, $part;
440 print "Unmount OSD $osdsection from $mountpoint\n";
441 eval { run_command
(['/bin/umount', $mountpoint]); };
444 } elsif ($param->{cleanup
}) {
445 #be aware of the ceph udev rules which can remount.
446 foreach my $part (@$partitions_to_remove) {
447 $remove_partition->($part);
449 my @wipe_cmd = qw(/bin/dd if=/dev/zero bs=1M count=200 conv=fdatasync);
450 foreach my $devpath (keys %$disks_to_wipe) {
451 print "wipe disk: $devpath\n";
452 eval { run_command
([@wipe_cmd, "of=${devpath}"]) };
458 return $rpcenv->fork_worker('cephdestroyosd', $osdsection, $authuser, $worker);
461 __PACKAGE__-
>register_method ({
463 path
=> '{osdid}/in',
465 description
=> "ceph osd in",
469 check
=> ['perm', '/', [ 'Sys.Modify' ]],
472 additionalProperties
=> 0,
474 node
=> get_standard_option
('pve-node'),
476 description
=> 'OSD ID',
481 returns
=> { type
=> "null" },
485 PVE
::CephTools
::check_ceph_inited
();
487 my $osdid = $param->{osdid
};
489 my $rados = PVE
::RADOS-
>new();
491 my $osdstat = &$get_osd_status($rados, $osdid); # osd exists?
493 my $osdsection = "osd.$osdid";
495 $rados->mon_command({ prefix
=> "osd in", ids
=> [ $osdsection ], format
=> 'plain' });
500 __PACKAGE__-
>register_method ({
502 path
=> '{osdid}/out',
504 description
=> "ceph osd out",
508 check
=> ['perm', '/', [ 'Sys.Modify' ]],
511 additionalProperties
=> 0,
513 node
=> get_standard_option
('pve-node'),
515 description
=> 'OSD ID',
520 returns
=> { type
=> "null" },
524 PVE
::CephTools
::check_ceph_inited
();
526 my $osdid = $param->{osdid
};
528 my $rados = PVE
::RADOS-
>new();
530 my $osdstat = &$get_osd_status($rados, $osdid); # osd exists?
532 my $osdsection = "osd.$osdid";
534 $rados->mon_command({ prefix
=> "osd out", ids
=> [ $osdsection ], format
=> 'plain' });
539 package PVE
::API2
::Ceph
;
545 use POSIX qw
(LONG_MAX
);
546 use Cwd
qw(abs_path);
552 use PVE
::Tools
qw(extract_param run_command file_get_contents file_read_firstline dir_glob_regex dir_glob_foreach);
553 use PVE
::Exception
qw(raise raise_param_exc);
555 use PVE
::Cluster
qw(cfs_lock_file cfs_read_file cfs_write_file);
556 use PVE
::AccessControl
;
558 use PVE
::RESTHandler
;
559 use PVE
::RPCEnvironment
;
560 use PVE
::JSONSchema
qw(get_standard_option);
566 use base
qw(PVE::RESTHandler);
568 use Data
::Dumper
; # fixme: remove
570 my $pve_osd_default_journal_size = 1024*5;
572 __PACKAGE__-
>register_method ({
573 subclass
=> "PVE::API2::CephOSD",
577 __PACKAGE__-
>register_method ({
581 description
=> "Directory index.",
582 permissions
=> { user
=> 'all' },
584 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
587 additionalProperties
=> 0,
589 node
=> get_standard_option
('pve-node'),
598 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
610 { name
=> 'status' },
612 { name
=> 'config' },
622 __PACKAGE__-
>register_method ({
626 description
=> "List local disks.",
630 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
633 additionalProperties
=> 0,
635 node
=> get_standard_option
('pve-node'),
637 description
=> "Only list specific types of disks.",
639 enum
=> ['unused', 'journal_disks'],
649 dev
=> { type
=> 'string' },
650 used
=> { type
=> 'string', optional
=> 1 },
651 gpt
=> { type
=> 'boolean' },
652 size
=> { type
=> 'integer' },
653 osdid
=> { type
=> 'integer' },
654 vendor
=> { type
=> 'string', optional
=> 1 },
655 model
=> { type
=> 'string', optional
=> 1 },
656 serial
=> { type
=> 'string', optional
=> 1 },
659 # links => [ { rel => 'child', href => "{}" } ],
664 PVE
::CephTools
::check_ceph_inited
();
666 my $disks = PVE
::Diskmanage
::get_disks
(undef, 1);
669 foreach my $dev (keys %$disks) {
670 my $d = $disks->{$dev};
671 if ($param->{type
}) {
672 if ($param->{type
} eq 'journal_disks') {
673 next if $d->{osdid
} >= 0;
675 } elsif ($param->{type
} eq 'unused') {
678 die "internal error"; # should not happen
682 $d->{dev
} = "/dev/$dev";
689 __PACKAGE__-
>register_method ({
694 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
696 description
=> "Get Ceph configuration.",
698 additionalProperties
=> 0,
700 node
=> get_standard_option
('pve-node'),
703 returns
=> { type
=> 'string' },
707 PVE
::CephTools
::check_ceph_inited
();
709 my $path = PVE
::CephTools
::get_config
('pve_ceph_cfgpath');
710 return PVE
::Tools
::file_get_contents
($path);
714 my $add_storage = sub {
715 my ($pool, $storeid) = @_;
717 my $storage_params = {
722 content
=> 'rootdir,images',
725 PVE
::API2
::Storage
::Config-
>create($storage_params);
728 my $get_storages = sub {
731 my $cfg = PVE
::Storage
::config
();
733 my $storages = $cfg->{ids
};
735 foreach my $storeid (keys %$storages) {
736 my $curr = $storages->{$storeid};
737 $res->{$storeid} = $storages->{$storeid}
738 if $curr->{type
} eq 'rbd' && $pool eq $curr->{pool
};
744 __PACKAGE__-
>register_method ({
748 description
=> "Get Ceph monitor list.",
752 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
755 additionalProperties
=> 0,
757 node
=> get_standard_option
('pve-node'),
765 name
=> { type
=> 'string' },
766 addr
=> { type
=> 'string' },
769 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
774 PVE
::CephTools
::check_ceph_inited
();
778 my $cfg = PVE
::CephTools
::parse_ceph_config
();
781 foreach my $section (keys %$cfg) {
782 my $d = $cfg->{$section};
783 if ($section =~ m/^mon\.(\S+)$/) {
785 if ($d->{'mon addr'} && $d->{'host'}) {
786 $monhash->{$monid} = {
787 addr
=> $d->{'mon addr'},
788 host
=> $d->{'host'},
796 my $rados = PVE
::RADOS-
>new();
797 my $monstat = $rados->mon_command({ prefix
=> 'mon_status' });
798 my $mons = $monstat->{monmap
}->{mons
};
799 foreach my $d (@$mons) {
800 next if !defined($d->{name
});
801 $monhash->{$d->{name
}}->{rank
} = $d->{rank
};
802 $monhash->{$d->{name
}}->{addr
} = $d->{addr
};
803 if (grep { $_ eq $d->{rank
} } @{$monstat->{quorum
}}) {
804 $monhash->{$d->{name
}}->{quorum
} = 1;
810 return PVE
::RESTHandler
::hash_to_array
($monhash, 'name');
813 __PACKAGE__-
>register_method ({
817 description
=> "Create initial ceph default configuration and setup symlinks.",
821 check
=> ['perm', '/', [ 'Sys.Modify' ]],
824 additionalProperties
=> 0,
826 node
=> get_standard_option
('pve-node'),
828 description
=> "Use specific network for all ceph related traffic",
829 type
=> 'string', format
=> 'CIDR',
834 description
=> 'Targeted number of replicas per object',
842 description
=> 'Minimum number of available replicas per object to allow I/O',
850 description
=> "Placement group bits, used to specify the " .
851 "default number of placement groups.\n\nNOTE: 'osd pool " .
852 "default pg num' does not work for default pools.",
860 description
=> "Disable cephx authentification.\n\n" .
861 "WARNING: cephx is a security feature protecting against " .
862 "man-in-the-middle attacks. Only consider disabling cephx ".
863 "if your network is private!",
870 returns
=> { type
=> 'null' },
874 my $version = PVE
::CephTools
::get_local_version
(1);
876 if (!$version || $version < 12) {
877 die "Ceph Luminous required - please run 'pveceph install'\n";
879 PVE
::CephTools
::check_ceph_installed
('ceph_bin');
882 # simply load old config if it already exists
883 my $cfg = PVE
::CephTools
::parse_ceph_config
();
885 if (!$cfg->{global
}) {
890 UUID
::generate
($uuid);
891 UUID
::unparse
($uuid, $fsid);
893 my $auth = $param->{disable_cephx
} ?
'none' : 'cephx';
897 'auth cluster required' => $auth,
898 'auth service required' => $auth,
899 'auth client required' => $auth,
900 'osd journal size' => $pve_osd_default_journal_size,
901 'osd pool default size' => $param->{size
} // 3,
902 'osd pool default min size' => $param->{min_size
} // 2,
903 'mon allow pool delete' => 'true',
906 # this does not work for default pools
907 #'osd pool default pg num' => $pg_num,
908 #'osd pool default pgp num' => $pg_num,
911 $cfg->{global
}->{keyring
} = '/etc/pve/priv/$cluster.$name.keyring';
912 $cfg->{osd
}->{keyring
} = '/var/lib/ceph/osd/ceph-$id/keyring';
914 if ($param->{pg_bits
}) {
915 $cfg->{global
}->{'osd pg bits'} = $param->{pg_bits
};
916 $cfg->{global
}->{'osd pgp bits'} = $param->{pg_bits
};
919 if ($param->{network
}) {
920 $cfg->{global
}->{'public network'} = $param->{network
};
921 $cfg->{global
}->{'cluster network'} = $param->{network
};
924 PVE
::CephTools
::write_ceph_config
($cfg);
926 PVE
::CephTools
::setup_pve_symlinks
();
931 my $find_mon_ip = sub {
932 my ($pubnet, $node, $overwrite_ip) = @_;
935 return $overwrite_ip // PVE
::Cluster
::remote_node_ip
($node);
938 my $allowed_ips = PVE
::Network
::get_local_ip_from_cidr
($pubnet);
939 die "No IP configured and up from ceph public network '$pubnet'\n"
940 if scalar(@$allowed_ips) < 1;
942 if (!$overwrite_ip) {
943 if (scalar(@$allowed_ips) == 1) {
944 return $allowed_ips->[0];
946 die "Multiple IPs for ceph public network '$pubnet' detected on $node:\n".
947 join("\n", @$allowed_ips) ."\nuse 'mon-address' to specify one of them.\n";
949 if (grep { $_ eq $overwrite_ip } @$allowed_ips) {
950 return $overwrite_ip;
952 die "Monitor IP '$overwrite_ip' not in ceph public network '$pubnet'\n"
953 if !PVE
::Network
::is_ip_in_cidr
($overwrite_ip, $pubnet);
955 die "Specified monitor IP '$overwrite_ip' not configured or up on $node!\n";
959 my $create_mgr = sub {
960 my ($rados, $id) = @_;
962 my $clustername = PVE
::CephTools
::get_config
('ccname');
963 my $mgrdir = "/var/lib/ceph/mgr/$clustername-$id";
964 my $mgrkeyring = "$mgrdir/keyring";
965 my $mgrname = "mgr.$id";
967 die "ceph manager directory '$mgrdir' already exists\n"
970 print "creating manager directory '$mgrdir'\n";
972 print "creating keys for '$mgrname'\n";
973 my $output = $rados->mon_command({ prefix
=> 'auth get-or-create',
976 mon
=> 'allow profile mgr',
981 PVE
::Tools
::file_set_contents
($mgrkeyring, $output);
983 print "setting owner for directory\n";
984 run_command
(["chown", 'ceph:ceph', '-R', $mgrdir]);
986 print "enabling service 'ceph-mgr\@$id.service'\n";
987 PVE
::CephTools
::ceph_service_cmd
('enable', $mgrname);
988 print "starting service 'ceph-mgr\@$id.service'\n";
989 PVE
::CephTools
::ceph_service_cmd
('start', $mgrname);
992 my $destroy_mgr = sub {
995 my $clustername = PVE
::CephTools
::get_config
('ccname');
996 my $mgrname = "mgr.$mgrid";
997 my $mgrdir = "/var/lib/ceph/mgr/$clustername-$mgrid";
999 die "ceph manager directory '$mgrdir' not found\n"
1002 print "disabling service 'ceph-mgr\@$mgrid.service'\n";
1003 PVE
::CephTools
::ceph_service_cmd
('disable', $mgrname);
1004 print "stopping service 'ceph-mgr\@$mgrid.service'\n";
1005 PVE
::CephTools
::ceph_service_cmd
('stop', $mgrname);
1007 print "removing manager directory '$mgrdir'\n";
1008 File
::Path
::remove_tree
($mgrdir);
1011 __PACKAGE__-
>register_method ({
1012 name
=> 'createmon',
1015 description
=> "Create Ceph Monitor and Manager",
1019 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1022 additionalProperties
=> 0,
1024 node
=> get_standard_option
('pve-node'),
1028 pattern
=> '[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?',
1029 description
=> "The ID for the monitor, when omitted the same as the nodename",
1031 'exclude-manager' => {
1035 description
=> "When set, only a monitor will be created.",
1038 description
=> 'Overwrites autodetected monitor IP address. ' .
1039 'Must be in the public network of ceph.',
1040 type
=> 'string', format
=> 'ip',
1045 returns
=> { type
=> 'string' },
1049 PVE
::CephTools
::check_ceph_installed
('ceph_mon');
1051 PVE
::CephTools
::check_ceph_installed
('ceph_mgr')
1052 if (!$param->{'exclude-manager'});
1054 PVE
::CephTools
::check_ceph_inited
();
1056 PVE
::CephTools
::setup_pve_symlinks
();
1058 my $rpcenv = PVE
::RPCEnvironment
::get
();
1060 my $authuser = $rpcenv->get_user();
1062 my $cfg = PVE
::CephTools
::parse_ceph_config
();
1066 my $monaddrhash = {};
1068 my $systemd_managed = PVE
::CephTools
::systemd_managed
();
1070 foreach my $section (keys %$cfg) {
1071 next if $section eq 'global';
1072 my $d = $cfg->{$section};
1073 if ($section =~ m/^mon\./) {
1075 if ($d->{'mon addr'}) {
1076 $monaddrhash->{$d->{'mon addr'}} = $section;
1081 my $monid = $param->{id
} // $param->{node
};
1083 my $monsection = "mon.$monid";
1084 my $pubnet = $cfg->{global
}->{'public network'};
1085 my $ip = $find_mon_ip->($pubnet, $param->{node
}, $param->{'mon-address'});
1087 my $monaddr = Net
::IP
::ip_is_ipv6
($ip) ?
"[$ip]:6789" : "$ip:6789";
1088 my $monname = $param->{node
};
1090 die "monitor '$monsection' already exists\n" if $cfg->{$monsection};
1091 die "monitor address '$monaddr' already in use by '$monaddrhash->{$monaddr}'\n"
1092 if $monaddrhash->{$monaddr};
1097 my $pve_ckeyring_path = PVE
::CephTools
::get_config
('pve_ckeyring_path');
1099 if (! -f
$pve_ckeyring_path) {
1100 run_command
("ceph-authtool $pve_ckeyring_path --create-keyring " .
1101 "--gen-key -n client.admin");
1104 my $pve_mon_key_path = PVE
::CephTools
::get_config
('pve_mon_key_path');
1105 if (! -f
$pve_mon_key_path) {
1106 run_command
("cp $pve_ckeyring_path $pve_mon_key_path.tmp");
1107 run_command
("ceph-authtool $pve_mon_key_path.tmp -n client.admin --set-uid=0 " .
1108 "--cap mds 'allow' " .
1109 "--cap osd 'allow *' " .
1110 "--cap mgr 'allow *' " .
1111 "--cap mon 'allow *'");
1112 run_command
("cp $pve_mon_key_path.tmp /etc/ceph/ceph.client.admin.keyring") if $systemd_managed;
1113 run_command
("chown ceph:ceph /etc/ceph/ceph.client.admin.keyring") if $systemd_managed;
1114 run_command
("ceph-authtool $pve_mon_key_path.tmp --gen-key -n mon. --cap mon 'allow *'");
1115 run_command
("mv $pve_mon_key_path.tmp $pve_mon_key_path");
1118 my $ccname = PVE
::CephTools
::get_config
('ccname');
1120 my $mondir = "/var/lib/ceph/mon/$ccname-$monid";
1121 -d
$mondir && die "monitor filesystem '$mondir' already exist\n";
1123 my $monmap = "/tmp/monmap";
1128 run_command
("chown ceph:ceph $mondir") if $systemd_managed;
1130 if ($moncount > 0) {
1131 my $rados = PVE
::RADOS-
>new(timeout
=> PVE
::CephTools
::get_config
('long_rados_timeout'));
1132 my $mapdata = $rados->mon_command({ prefix
=> 'mon getmap', format
=> 'plain' });
1133 PVE
::Tools
::file_set_contents
($monmap, $mapdata);
1135 run_command
("monmaptool --create --clobber --add $monid $monaddr --print $monmap");
1138 run_command
("ceph-mon --mkfs -i $monid --monmap $monmap --keyring $pve_mon_key_path");
1139 run_command
("chown ceph:ceph -R $mondir") if $systemd_managed;
1144 File
::Path
::remove_tree
($mondir);
1148 $cfg->{$monsection} = {
1150 'mon addr' => $monaddr,
1153 PVE
::CephTools
::write_ceph_config
($cfg);
1155 my $create_keys_pid = fork();
1156 if (!defined($create_keys_pid)) {
1157 die "Could not spawn ceph-create-keys to create bootstrap keys\n";
1158 } elsif ($create_keys_pid == 0) {
1159 exit PVE
::Tools
::run_command
(['ceph-create-keys', '-i', $monid]);
1161 PVE
::CephTools
::ceph_service_cmd
('start', $monsection);
1163 if ($systemd_managed) {
1164 #to ensure we have the correct startup order.
1165 eval { PVE
::Tools
::run_command
(['/bin/systemctl', 'enable', "ceph-mon\@${monid}.service"]); };
1166 warn "Enable ceph-mon\@${monid}.service manually"if $@;
1168 waitpid($create_keys_pid, 0);
1172 if (!$param->{'exclude-manager'}) {
1173 my $rados = PVE
::RADOS-
>new(timeout
=> PVE
::CephTools
::get_config
('long_rados_timeout'));
1174 $create_mgr->($rados, $monid);
1178 return $rpcenv->fork_worker('cephcreatemon', $monsection, $authuser, $worker);
1181 __PACKAGE__-
>register_method ({
1182 name
=> 'destroymon',
1183 path
=> 'mon/{monid}',
1185 description
=> "Destroy Ceph Monitor and Manager.",
1189 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1192 additionalProperties
=> 0,
1194 node
=> get_standard_option
('pve-node'),
1196 description
=> 'Monitor ID',
1198 pattern
=> '[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?',
1200 'exclude-manager' => {
1204 description
=> "When set, removes only the monitor, not the manager"
1208 returns
=> { type
=> 'string' },
1212 my $rpcenv = PVE
::RPCEnvironment
::get
();
1214 my $authuser = $rpcenv->get_user();
1216 PVE
::CephTools
::check_ceph_inited
();
1218 my $cfg = PVE
::CephTools
::parse_ceph_config
();
1220 my $monid = $param->{monid
};
1221 my $monsection = "mon.$monid";
1223 my $rados = PVE
::RADOS-
>new();
1224 my $monstat = $rados->mon_command({ prefix
=> 'mon_status' });
1225 my $monlist = $monstat->{monmap
}->{mons
};
1227 die "no such monitor id '$monid'\n"
1228 if !defined($cfg->{$monsection});
1230 my $ccname = PVE
::CephTools
::get_config
('ccname');
1232 my $mondir = "/var/lib/ceph/mon/$ccname-$monid";
1233 -d
$mondir || die "monitor filesystem '$mondir' does not exist on this node\n";
1235 die "can't remove last monitor\n" if scalar(@$monlist) <= 1;
1240 # reopen with longer timeout
1241 $rados = PVE
::RADOS-
>new(timeout
=> PVE
::CephTools
::get_config
('long_rados_timeout'));
1243 $rados->mon_command({ prefix
=> "mon remove", name
=> $monid, format
=> 'plain' });
1245 eval { PVE
::CephTools
::ceph_service_cmd
('stop', $monsection); };
1248 delete $cfg->{$monsection};
1249 PVE
::CephTools
::write_ceph_config
($cfg);
1250 File
::Path
::remove_tree
($mondir);
1253 if (!$param->{'exclude-manager'}) {
1254 eval { $destroy_mgr->($monid); };
1259 return $rpcenv->fork_worker('cephdestroymon', $monsection, $authuser, $worker);
1262 __PACKAGE__-
>register_method ({
1263 name
=> 'createmgr',
1266 description
=> "Create Ceph Manager",
1270 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1273 additionalProperties
=> 0,
1275 node
=> get_standard_option
('pve-node'),
1279 pattern
=> '[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?',
1280 description
=> "The ID for the manager, when omitted the same as the nodename",
1284 returns
=> { type
=> 'string' },
1288 PVE
::CephTools
::check_ceph_installed
('ceph_mgr');
1290 PVE
::CephTools
::check_ceph_inited
();
1292 my $rpcenv = PVE
::RPCEnvironment
::get
();
1294 my $authuser = $rpcenv->get_user();
1296 my $mgrid = $param->{id
} // $param->{node
};
1301 my $rados = PVE
::RADOS-
>new(timeout
=> PVE
::CephTools
::get_config
('long_rados_timeout'));
1303 $create_mgr->($rados, $mgrid);
1306 return $rpcenv->fork_worker('cephcreatemgr', "mgr.$mgrid", $authuser, $worker);
1309 __PACKAGE__-
>register_method ({
1310 name
=> 'destroymgr',
1313 description
=> "Destroy Ceph Manager.",
1317 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1320 additionalProperties
=> 0,
1322 node
=> get_standard_option
('pve-node'),
1324 description
=> 'The ID of the manager',
1326 pattern
=> '[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?',
1330 returns
=> { type
=> 'string' },
1334 my $rpcenv = PVE
::RPCEnvironment
::get
();
1336 my $authuser = $rpcenv->get_user();
1338 PVE
::CephTools
::check_ceph_inited
();
1340 my $mgrid = $param->{id
};
1345 $destroy_mgr->($mgrid);
1348 return $rpcenv->fork_worker('cephdestroymgr', "mgr.$mgrid", $authuser, $worker);
1351 __PACKAGE__-
>register_method ({
1355 description
=> "Stop ceph services.",
1359 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1362 additionalProperties
=> 0,
1364 node
=> get_standard_option
('pve-node'),
1366 description
=> 'Ceph service name.',
1369 pattern
=> '(mon|mds|osd|mgr)\.[A-Za-z0-9\-]{1,32}',
1373 returns
=> { type
=> 'string' },
1377 my $rpcenv = PVE
::RPCEnvironment
::get
();
1379 my $authuser = $rpcenv->get_user();
1381 PVE
::CephTools
::check_ceph_inited
();
1383 my $cfg = PVE
::CephTools
::parse_ceph_config
();
1384 scalar(keys %$cfg) || die "no configuration\n";
1390 if ($param->{service
}) {
1391 push @$cmd, $param->{service
};
1394 PVE
::CephTools
::ceph_service_cmd
(@$cmd);
1397 return $rpcenv->fork_worker('srvstop', $param->{service
} || 'ceph',
1398 $authuser, $worker);
1401 __PACKAGE__-
>register_method ({
1405 description
=> "Start ceph services.",
1409 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1412 additionalProperties
=> 0,
1414 node
=> get_standard_option
('pve-node'),
1416 description
=> 'Ceph service name.',
1419 pattern
=> '(mon|mds|osd|mgr)\.[A-Za-z0-9\-]{1,32}',
1423 returns
=> { type
=> 'string' },
1427 my $rpcenv = PVE
::RPCEnvironment
::get
();
1429 my $authuser = $rpcenv->get_user();
1431 PVE
::CephTools
::check_ceph_inited
();
1433 my $cfg = PVE
::CephTools
::parse_ceph_config
();
1434 scalar(keys %$cfg) || die "no configuration\n";
1439 my $cmd = ['start'];
1440 if ($param->{service
}) {
1441 push @$cmd, $param->{service
};
1444 PVE
::CephTools
::ceph_service_cmd
(@$cmd);
1447 return $rpcenv->fork_worker('srvstart', $param->{service
} || 'ceph',
1448 $authuser, $worker);
1451 __PACKAGE__-
>register_method ({
1455 description
=> "Get ceph status.",
1459 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
1462 additionalProperties
=> 0,
1464 node
=> get_standard_option
('pve-node'),
1467 returns
=> { type
=> 'object' },
1471 PVE
::CephTools
::check_ceph_enabled
();
1473 my $rados = PVE
::RADOS-
>new();
1474 my $status = $rados->mon_command({ prefix
=> 'status' });
1475 $status->{health
} = $rados->mon_command({ prefix
=> 'health', detail
=> 'detail' });
1479 __PACKAGE__-
>register_method ({
1483 description
=> "List all pools.",
1487 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
1490 additionalProperties
=> 0,
1492 node
=> get_standard_option
('pve-node'),
1500 pool
=> { type
=> 'integer' },
1501 pool_name
=> { type
=> 'string' },
1502 size
=> { type
=> 'integer' },
1505 links
=> [ { rel
=> 'child', href
=> "{pool_name}" } ],
1510 PVE
::CephTools
::check_ceph_inited
();
1512 my $rados = PVE
::RADOS-
>new();
1515 my $res = $rados->mon_command({ prefix
=> 'df' });
1517 foreach my $d (@{$res->{pools
}}) {
1518 next if !$d->{stats
};
1519 next if !defined($d->{id
});
1520 $stats->{$d->{id
}} = $d->{stats
};
1523 $res = $rados->mon_command({ prefix
=> 'osd dump' });
1524 my $rulestmp = $rados->mon_command({ prefix
=> 'osd crush rule dump'});
1527 for my $rule (@$rulestmp) {
1528 $rules->{$rule->{rule_id
}} = $rule->{rule_name
};
1532 foreach my $e (@{$res->{pools
}}) {
1534 foreach my $attr (qw(pool pool_name size min_size pg_num crush_rule)) {
1535 $d->{$attr} = $e->{$attr} if defined($e->{$attr});
1538 if (defined($d->{crush_rule
}) && defined($rules->{$d->{crush_rule
}})) {
1539 $d->{crush_rule_name
} = $rules->{$d->{crush_rule
}};
1542 if (my $s = $stats->{$d->{pool
}}) {
1543 $d->{bytes_used
} = $s->{bytes_used
};
1544 $d->{percent_used
} = $s->{percent_used
};
1553 __PACKAGE__-
>register_method ({
1554 name
=> 'createpool',
1557 description
=> "Create POOL",
1561 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1564 additionalProperties
=> 0,
1566 node
=> get_standard_option
('pve-node'),
1568 description
=> "The name of the pool. It must be unique.",
1572 description
=> 'Number of replicas per object',
1580 description
=> 'Minimum number of replicas per object',
1588 description
=> "Number of placement groups.",
1596 description
=> "The rule to use for mapping object placement in the cluster.",
1601 description
=> "The application of the pool, 'rbd' by default.",
1603 enum
=> ['rbd', 'cephfs', 'rgw'],
1607 description
=> "Configure VM and CT storage using the new pool.",
1613 returns
=> { type
=> 'string' },
1617 PVE
::Cluster
::check_cfs_quorum
();
1618 PVE
::CephTools
::check_ceph_inited
();
1620 my $pve_ckeyring_path = PVE
::CephTools
::get_config
('pve_ckeyring_path');
1622 die "not fully configured - missing '$pve_ckeyring_path'\n"
1623 if ! -f
$pve_ckeyring_path;
1625 my $pool = $param->{name
};
1626 my $rpcenv = PVE
::RPCEnvironment
::get
();
1627 my $user = $rpcenv->get_user();
1629 if ($param->{add_storages
}) {
1630 $rpcenv->check($user, '/storage', ['Datastore.Allocate']);
1631 die "pool name contains characters which are illegal for storage naming\n"
1632 if !PVE
::JSONSchema
::parse_storage_id
($pool);
1635 my $pg_num = $param->{pg_num
} || 64;
1636 my $size = $param->{size
} || 3;
1637 my $min_size = $param->{min_size
} || 2;
1638 my $application = $param->{application
} // 'rbd';
1642 my $rados = PVE
::RADOS-
>new();
1643 $rados->mon_command({
1644 prefix
=> "osd pool create",
1646 pg_num
=> int($pg_num),
1650 $rados->mon_command({
1651 prefix
=> "osd pool set",
1658 $rados->mon_command({
1659 prefix
=> "osd pool set",
1666 if (defined($param->{crush_rule
})) {
1667 $rados->mon_command({
1668 prefix
=> "osd pool set",
1670 var
=> 'crush_rule',
1671 val
=> $param->{crush_rule
},
1676 $rados->mon_command({
1677 prefix
=> "osd pool application enable",
1679 app
=> $application,
1682 if ($param->{add_storages
}) {
1684 eval { $add_storage->($pool, "${pool}"); };
1686 warn "failed to add storage: $@";
1689 die "adding storage for pool '$pool' failed, check log and add manually!\n"
1694 return $rpcenv->fork_worker('cephcreatepool', $pool, $user, $worker);
1697 __PACKAGE__-
>register_method ({
1698 name
=> 'get_flags',
1701 description
=> "get all set ceph flags",
1705 check
=> ['perm', '/', [ 'Sys.Audit' ]],
1708 additionalProperties
=> 0,
1710 node
=> get_standard_option
('pve-node'),
1713 returns
=> { type
=> 'string' },
1717 PVE
::CephTools
::check_ceph_inited
();
1719 my $pve_ckeyring_path = PVE
::CephTools
::get_config
('pve_ckeyring_path');
1721 die "not fully configured - missing '$pve_ckeyring_path'\n"
1722 if ! -f
$pve_ckeyring_path;
1724 my $rados = PVE
::RADOS-
>new();
1726 my $stat = $rados->mon_command({ prefix
=> 'osd dump' });
1728 return $stat->{flags
} // '';
1731 __PACKAGE__-
>register_method ({
1733 path
=> 'flags/{flag}',
1735 description
=> "Set a ceph flag",
1739 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1742 additionalProperties
=> 0,
1744 node
=> get_standard_option
('pve-node'),
1746 description
=> 'The ceph flag to set/unset',
1748 enum
=> [ 'full', 'pause', 'noup', 'nodown', 'noout', 'noin', 'nobackfill', 'norebalance', 'norecover', 'noscrub', 'nodeep-scrub', 'notieragent'],
1752 returns
=> { type
=> 'null' },
1756 PVE
::CephTools
::check_ceph_inited
();
1758 my $pve_ckeyring_path = PVE
::CephTools
::get_config
('pve_ckeyring_path');
1760 die "not fully configured - missing '$pve_ckeyring_path'\n"
1761 if ! -f
$pve_ckeyring_path;
1763 my $set = $param->{set
} // !$param->{unset
};
1764 my $rados = PVE
::RADOS-
>new();
1766 $rados->mon_command({
1767 prefix
=> "osd set",
1768 key
=> $param->{flag
},
1774 __PACKAGE__-
>register_method ({
1775 name
=> 'unset_flag',
1776 path
=> 'flags/{flag}',
1778 description
=> "Unset a ceph flag",
1782 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1785 additionalProperties
=> 0,
1787 node
=> get_standard_option
('pve-node'),
1789 description
=> 'The ceph flag to set/unset',
1791 enum
=> [ 'full', 'pause', 'noup', 'nodown', 'noout', 'noin', 'nobackfill', 'norebalance', 'norecover', 'noscrub', 'nodeep-scrub', 'notieragent'],
1795 returns
=> { type
=> 'null' },
1799 PVE
::CephTools
::check_ceph_inited
();
1801 my $pve_ckeyring_path = PVE
::CephTools
::get_config
('pve_ckeyring_path');
1803 die "not fully configured - missing '$pve_ckeyring_path'\n"
1804 if ! -f
$pve_ckeyring_path;
1806 my $set = $param->{set
} // !$param->{unset
};
1807 my $rados = PVE
::RADOS-
>new();
1809 $rados->mon_command({
1810 prefix
=> "osd unset",
1811 key
=> $param->{flag
},
1817 __PACKAGE__-
>register_method ({
1818 name
=> 'destroypool',
1819 path
=> 'pools/{name}',
1821 description
=> "Destroy pool",
1825 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1828 additionalProperties
=> 0,
1830 node
=> get_standard_option
('pve-node'),
1832 description
=> "The name of the pool. It must be unique.",
1836 description
=> "If true, destroys pool even if in use",
1841 remove_storages
=> {
1842 description
=> "Remove all pveceph-managed storages configured for this pool",
1849 returns
=> { type
=> 'string' },
1853 PVE
::CephTools
::check_ceph_inited
();
1855 my $rpcenv = PVE
::RPCEnvironment
::get
();
1856 my $user = $rpcenv->get_user();
1857 $rpcenv->check($user, '/storage', ['Datastore.Allocate'])
1858 if $param->{remove_storages
};
1860 my $pool = $param->{name
};
1863 my $storages = $get_storages->($pool);
1865 # if not forced, destroy ceph pool only when no
1866 # vm disks are on it anymore
1867 if (!$param->{force
}) {
1868 my $storagecfg = PVE
::Storage
::config
();
1869 foreach my $storeid (keys %$storages) {
1870 my $storage = $storages->{$storeid};
1872 # check if any vm disks are on the pool
1873 print "checking storage '$storeid' for RBD images..\n";
1874 my $res = PVE
::Storage
::vdisk_list
($storagecfg, $storeid);
1875 die "ceph pool '$pool' still in use by storage '$storeid'\n"
1876 if @{$res->{$storeid}} != 0;
1880 my $rados = PVE
::RADOS-
>new();
1881 # fixme: '--yes-i-really-really-mean-it'
1882 $rados->mon_command({
1883 prefix
=> "osd pool delete",
1886 sure
=> '--yes-i-really-really-mean-it',
1890 if ($param->{remove_storages
}) {
1892 foreach my $storeid (keys %$storages) {
1893 # skip external clusters, not managed by pveceph
1894 next if $storages->{$storeid}->{monhost
};
1895 eval { PVE
::API2
::Storage
::Config-
>delete({storage
=> $storeid}) };
1897 warn "failed to remove storage '$storeid': $@\n";
1901 die "failed to remove (some) storages - check log and remove manually!\n"
1905 return $rpcenv->fork_worker('cephdestroypool', $pool, $user, $worker);
1909 __PACKAGE__-
>register_method ({
1913 description
=> "Get OSD crush map",
1917 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
1920 additionalProperties
=> 0,
1922 node
=> get_standard_option
('pve-node'),
1925 returns
=> { type
=> 'string' },
1929 PVE
::CephTools
::check_ceph_inited
();
1931 # this produces JSON (difficult to read for the user)
1932 # my $txt = &$run_ceph_cmd_text(['osd', 'crush', 'dump'], quiet => 1);
1936 my $mapfile = "/var/tmp/ceph-crush.map.$$";
1937 my $mapdata = "/var/tmp/ceph-crush.txt.$$";
1939 my $rados = PVE
::RADOS-
>new();
1942 my $bindata = $rados->mon_command({ prefix
=> 'osd getcrushmap', format
=> 'plain' });
1943 PVE
::Tools
::file_set_contents
($mapfile, $bindata);
1944 run_command
(['crushtool', '-d', $mapfile, '-o', $mapdata]);
1945 $txt = PVE
::Tools
::file_get_contents
($mapdata);
1957 __PACKAGE__-
>register_method({
1961 description
=> "Read ceph log",
1964 check
=> ['perm', '/nodes/{node}', [ 'Sys.Syslog' ]],
1968 additionalProperties
=> 0,
1970 node
=> get_standard_option
('pve-node'),
1989 description
=> "Line number",
1993 description
=> "Line text",
2002 my $rpcenv = PVE
::RPCEnvironment
::get
();
2003 my $user = $rpcenv->get_user();
2004 my $node = $param->{node
};
2006 my $logfile = "/var/log/ceph/ceph.log";
2007 my ($count, $lines) = PVE
::Tools
::dump_logfile
($logfile, $param->{start
}, $param->{limit
});
2009 $rpcenv->set_result_attrib('total', $count);
2014 __PACKAGE__-
>register_method ({
2018 description
=> "List ceph rules.",
2022 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
2025 additionalProperties
=> 0,
2027 node
=> get_standard_option
('pve-node'),
2036 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
2041 PVE
::CephTools
::check_ceph_inited
();
2043 my $rados = PVE
::RADOS-
>new();
2045 my $rules = $rados->mon_command({ prefix
=> 'osd crush rule ls' });
2049 foreach my $rule (@$rules) {
2050 push @$res, { name
=> $rule };