]>
git.proxmox.com Git - pve-manager.git/blob - PVE/API2/Ceph.pm
1 package PVE
::API2
::CephOSD
;
11 use PVE
::Exception
qw(raise_param_exc);
12 use PVE
::JSONSchema
qw(get_standard_option);
15 use PVE
::RPCEnvironment
;
16 use PVE
::Tools
qw(run_command);
18 use base
qw(PVE::RESTHandler);
20 my $get_osd_status = sub {
21 my ($rados, $osdid) = @_;
23 my $stat = $rados->mon_command({ prefix
=> 'osd dump' });
25 my $osdlist = $stat->{osds
} || [];
27 my $flags = $stat->{flags
} || undef;
30 foreach my $d (@$osdlist) {
31 $osdstat->{$d->{osd
}} = $d if defined($d->{osd
});
33 if (defined($osdid)) {
34 die "no such OSD '$osdid'\n" if !$osdstat->{$osdid};
35 return $osdstat->{$osdid};
38 return wantarray?
($osdstat, $flags):$osdstat;
41 my $get_osd_usage = sub {
44 my $osdlist = $rados->mon_command({ prefix
=> 'pg dump',
45 dumpcontents
=> [ 'osds' ]}) || [];
48 foreach my $d (@$osdlist) {
49 $osdstat->{$d->{osd
}} = $d if defined($d->{osd
});
55 __PACKAGE__-
>register_method ({
59 description
=> "Get Ceph osd list/tree.",
63 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
66 additionalProperties
=> 0,
68 node
=> get_standard_option
('pve-node'),
71 # fixme: return a list instead of extjs tree format ?
78 PVE
::CephTools
::check_ceph_inited
();
80 my $rados = PVE
::RADOS-
>new();
81 my $res = $rados->mon_command({ prefix
=> 'osd tree' });
83 die "no tree nodes found\n" if !($res && $res->{nodes
});
85 my ($osdhash, $flags) = &$get_osd_status($rados);
87 my $usagehash = &$get_osd_usage($rados);
89 my $osdmetadata_tmp = $rados->mon_command({ prefix
=> 'osd metadata' });
92 foreach my $osd (@$osdmetadata_tmp) {
93 $osdmetadata->{$osd->{id
}} = $osd;
98 foreach my $e (@{$res->{nodes
}}) {
99 $nodes->{$e->{id
}} = $e;
107 foreach my $opt (qw(status crush_weight reweight device_class)) {
108 $new->{$opt} = $e->{$opt} if defined($e->{$opt});
111 if (my $stat = $osdhash->{$e->{id
}}) {
112 $new->{in} = $stat->{in} if defined($stat->{in});
115 if (my $stat = $usagehash->{$e->{id
}}) {
116 $new->{total_space
} = ($stat->{kb
} || 1) * 1024;
117 $new->{bytes_used
} = ($stat->{kb_used
} || 0) * 1024;
118 $new->{percent_used
} = ($new->{bytes_used
}*100)/$new->{total_space
};
119 if (my $d = $stat->{perf_stat
}) {
120 $new->{commit_latency_ms
} = $d->{commit_latency_ms
};
121 $new->{apply_latency_ms
} = $d->{apply_latency_ms
};
125 my $osdmd = $osdmetadata->{$e->{id
}};
126 if ($e->{type
} eq 'osd' && $osdmd) {
127 if ($osdmd->{bluefs
}) {
128 $new->{osdtype
} = 'bluestore';
129 $new->{blfsdev
} = $osdmd->{bluestore_bdev_dev_node
};
130 $new->{dbdev
} = $osdmd->{bluefs_db_dev_node
};
131 $new->{waldev
} = $osdmd->{bluefs_wal_dev_node
};
133 $new->{osdtype
} = 'filestore';
137 $newnodes->{$e->{id
}} = $new;
140 foreach my $e (@{$res->{nodes
}}) {
141 my $new = $newnodes->{$e->{id
}};
142 if ($e->{children
} && scalar(@{$e->{children
}})) {
143 $new->{children
} = [];
145 foreach my $cid (@{$e->{children
}}) {
146 $nodes->{$cid}->{parent
} = $e->{id
};
147 if ($nodes->{$cid}->{type
} eq 'osd' &&
148 $e->{type
} eq 'host') {
149 $newnodes->{$cid}->{host
} = $e->{name
};
151 push @{$new->{children
}}, $newnodes->{$cid};
154 $new->{leaf
} = ($e->{id
} >= 0) ?
1 : 0;
159 foreach my $e (@{$res->{nodes
}}) {
160 if (!$nodes->{$e->{id
}}->{parent
}) {
161 push @$roots, $newnodes->{$e->{id
}};
165 die "no root node\n" if !@$roots;
167 my $data = { root
=> { leaf
=> 0, children
=> $roots } };
169 # we want this for the noout flag
170 $data->{flags
} = $flags if $flags;
175 __PACKAGE__-
>register_method ({
179 description
=> "Create OSD",
183 additionalProperties
=> 0,
185 node
=> get_standard_option
('pve-node'),
187 description
=> "Block device name.",
191 description
=> "Block device name for journal (filestore) or block.db (bluestore).",
196 description
=> "Block device name for block.wal (bluestore only).",
201 description
=> "File system type (filestore only).",
203 enum
=> ['xfs', 'ext4'],
208 description
=> "Use bluestore instead of filestore. This is the default.",
215 returns
=> { type
=> 'string' },
219 my $rpcenv = PVE
::RPCEnvironment
::get
();
221 my $authuser = $rpcenv->get_user();
223 raise_param_exc
({ 'bluestore' => "conflicts with parameter 'fstype'" })
224 if (defined($param->{fstype
}) && defined($param->{bluestore
}) && $param->{bluestore
});
226 PVE
::CephTools
::check_ceph_inited
();
228 PVE
::CephTools
::setup_pve_symlinks
();
230 PVE
::CephTools
::check_ceph_installed
('ceph_osd');
232 my $bluestore = $param->{bluestore
} // 1;
237 if ($param->{journal_dev
} && ($param->{journal_dev
} ne $param->{dev
})) {
238 $journal_dev = PVE
::Diskmanage
::verify_blockdev_path
($param->{journal_dev
});
241 if ($param->{wal_dev
} &&
242 ($param->{wal_dev
} ne $param->{dev
}) &&
243 (!$param->{journal_dev
} || $param->{wal_dev
} ne $param->{journal_dev
})) {
244 raise_param_exc
({ 'wal_dev' => "can only be set with paramater 'bluestore'"})
246 $wal_dev = PVE
::Diskmanage
::verify_blockdev_path
($param->{wal_dev
});
249 $param->{dev
} = PVE
::Diskmanage
::verify_blockdev_path
($param->{dev
});
251 my $devname = $param->{dev
};
252 $devname =~ s
|/dev/||;
254 my $disklist = PVE
::Diskmanage
::get_disks
($devname, 1);
256 my $diskinfo = $disklist->{$devname};
257 die "unable to get device info for '$devname'\n"
260 die "device '$param->{dev}' is in use\n"
261 if $diskinfo->{used
};
263 my $devpath = $diskinfo->{devpath
};
264 my $rados = PVE
::RADOS-
>new();
265 my $monstat = $rados->mon_command({ prefix
=> 'mon_status' });
266 die "unable to get fsid\n" if !$monstat->{monmap
} || !$monstat->{monmap
}->{fsid
};
268 my $fsid = $monstat->{monmap
}->{fsid
};
269 $fsid = $1 if $fsid =~ m/^([0-9a-f\-]+)$/;
271 my $ceph_bootstrap_osd_keyring = PVE
::CephTools
::get_config
('ceph_bootstrap_osd_keyring');
273 if (! -f
$ceph_bootstrap_osd_keyring) {
274 my $bindata = $rados->mon_command({ prefix
=> 'auth get', entity
=> 'client.bootstrap-osd', format
=> 'plain' });
275 file_set_contents
($ceph_bootstrap_osd_keyring, $bindata);
281 my $fstype = $param->{fstype
} || 'xfs';
284 my $ccname = PVE
::CephTools
::get_config
('ccname');
286 my $cmd = ['ceph-disk', 'prepare', '--zap-disk',
287 '--cluster', $ccname, '--cluster-uuid', $fsid ];
290 print "create OSD on $devpath (bluestore)\n";
291 push @$cmd, '--bluestore';
294 print "using device '$journal_dev' for block.db\n";
295 push @$cmd, '--block.db', $journal_dev;
299 print "using device '$wal_dev' for block.wal\n";
300 push @$cmd, '--block.wal', $wal_dev;
303 push @$cmd, $devpath;
305 print "create OSD on $devpath ($fstype)\n";
306 push @$cmd, '--filestore', '--fs-type', $fstype;
308 print "using device '$journal_dev' for journal\n";
309 push @$cmd, '--journal-dev', $devpath, $journal_dev;
311 push @$cmd, $devpath;
319 return $rpcenv->fork_worker('cephcreateosd', $devname, $authuser, $worker);
322 __PACKAGE__-
>register_method ({
323 name
=> 'destroyosd',
326 description
=> "Destroy OSD",
330 additionalProperties
=> 0,
332 node
=> get_standard_option
('pve-node'),
334 description
=> 'OSD ID',
338 description
=> "If set, we remove partition table entries.",
345 returns
=> { type
=> 'string' },
349 my $rpcenv = PVE
::RPCEnvironment
::get
();
351 my $authuser = $rpcenv->get_user();
353 PVE
::CephTools
::check_ceph_inited
();
355 my $osdid = $param->{osdid
};
357 my $rados = PVE
::RADOS-
>new();
358 my $osdstat = &$get_osd_status($rados, $osdid);
360 die "osd is in use (in == 1)\n" if $osdstat->{in};
361 #&$run_ceph_cmd(['osd', 'out', $osdid]);
363 die "osd is still runnung (up == 1)\n" if $osdstat->{up
};
365 my $osdsection = "osd.$osdid";
370 # reopen with longer timeout
371 $rados = PVE
::RADOS-
>new(timeout
=> PVE
::CephTools
::get_config
('long_rados_timeout'));
373 print "destroy OSD $osdsection\n";
376 PVE
::CephTools
::ceph_service_cmd
('stop', $osdsection);
377 PVE
::CephTools
::ceph_service_cmd
('disable', $osdsection);
381 print "Remove $osdsection from the CRUSH map\n";
382 $rados->mon_command({ prefix
=> "osd crush remove", name
=> $osdsection, format
=> 'plain' });
384 print "Remove the $osdsection authentication key.\n";
385 $rados->mon_command({ prefix
=> "auth del", entity
=> $osdsection, format
=> 'plain' });
387 print "Remove OSD $osdsection\n";
388 $rados->mon_command({ prefix
=> "osd rm", ids
=> [ $osdsection ], format
=> 'plain' });
390 # try to unmount from standard mount point
391 my $mountpoint = "/var/lib/ceph/osd/ceph-$osdid";
393 my $disks_to_wipe = {};
394 my $remove_partition = sub {
397 return if !$part || (! -b
$part );
398 my $partnum = PVE
::Diskmanage
::get_partnum
($part);
399 my $devpath = PVE
::Diskmanage
::get_blockdev
($part);
401 print "remove partition $part (disk '${devpath}', partnum $partnum)\n";
402 eval { run_command
(['/sbin/sgdisk', '-d', $partnum, "${devpath}"]); };
405 $disks_to_wipe->{$devpath} = 1;
408 my $partitions_to_remove = [];
410 if ($param->{cleanup
}) {
411 if (my $fd = IO
::File-
>new("/proc/mounts", "r")) {
412 while (defined(my $line = <$fd>)) {
413 my ($dev, $path, $fstype) = split(/\s+/, $line);
414 next if !($dev && $path && $fstype);
415 next if $dev !~ m
|^/dev/|;
416 if ($path eq $mountpoint) {
417 my $data_part = abs_path
($dev);
418 push @$partitions_to_remove, $data_part;
425 foreach my $path (qw(journal block block.db block.wal)) {
426 my $part = abs_path
("$mountpoint/$path");
428 push @$partitions_to_remove, $part;
433 print "Unmount OSD $osdsection from $mountpoint\n";
434 eval { run_command
(['/bin/umount', $mountpoint]); };
437 } elsif ($param->{cleanup
}) {
438 #be aware of the ceph udev rules which can remount.
439 foreach my $part (@$partitions_to_remove) {
440 $remove_partition->($part);
442 my @wipe_cmd = qw(/bin/dd if=/dev/zero bs=1M count=200 conv=fdatasync);
443 foreach my $devpath (keys %$disks_to_wipe) {
444 print "wipe disk: $devpath\n";
445 eval { run_command
([@wipe_cmd, "of=${devpath}"]) };
451 return $rpcenv->fork_worker('cephdestroyosd', $osdsection, $authuser, $worker);
454 __PACKAGE__-
>register_method ({
456 path
=> '{osdid}/in',
458 description
=> "ceph osd in",
462 check
=> ['perm', '/', [ 'Sys.Modify' ]],
465 additionalProperties
=> 0,
467 node
=> get_standard_option
('pve-node'),
469 description
=> 'OSD ID',
474 returns
=> { type
=> "null" },
478 PVE
::CephTools
::check_ceph_inited
();
480 my $osdid = $param->{osdid
};
482 my $rados = PVE
::RADOS-
>new();
484 my $osdstat = &$get_osd_status($rados, $osdid); # osd exists?
486 my $osdsection = "osd.$osdid";
488 $rados->mon_command({ prefix
=> "osd in", ids
=> [ $osdsection ], format
=> 'plain' });
493 __PACKAGE__-
>register_method ({
495 path
=> '{osdid}/out',
497 description
=> "ceph osd out",
501 check
=> ['perm', '/', [ 'Sys.Modify' ]],
504 additionalProperties
=> 0,
506 node
=> get_standard_option
('pve-node'),
508 description
=> 'OSD ID',
513 returns
=> { type
=> "null" },
517 PVE
::CephTools
::check_ceph_inited
();
519 my $osdid = $param->{osdid
};
521 my $rados = PVE
::RADOS-
>new();
523 my $osdstat = &$get_osd_status($rados, $osdid); # osd exists?
525 my $osdsection = "osd.$osdid";
527 $rados->mon_command({ prefix
=> "osd out", ids
=> [ $osdsection ], format
=> 'plain' });
532 package PVE
::API2
::Ceph
;
543 use PVE
::JSONSchema
qw(get_standard_option);
546 use PVE
::RESTHandler
;
547 use PVE
::RPCEnvironment
;
549 use PVE
::Tools
qw(run_command file_get_contents file_set_contents);
551 use PVE
::API2
::Ceph
::FS
;
552 use PVE
::API2
::Ceph
::MDS
;
553 use PVE
::API2
::Storage
::Config
;
555 use base
qw(PVE::RESTHandler);
557 my $pve_osd_default_journal_size = 1024*5;
559 __PACKAGE__-
>register_method ({
560 subclass
=> "PVE::API2::CephOSD",
564 __PACKAGE__-
>register_method ({
565 subclass
=> "PVE::API2::Ceph::MDS",
569 __PACKAGE__-
>register_method ({
570 subclass
=> "PVE::API2::Ceph::FS",
574 __PACKAGE__-
>register_method ({
578 description
=> "Directory index.",
579 permissions
=> { user
=> 'all' },
581 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
584 additionalProperties
=> 0,
586 node
=> get_standard_option
('pve-node'),
595 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
609 { name
=> 'status' },
611 { name
=> 'config' },
621 __PACKAGE__-
>register_method ({
625 description
=> "List local disks.",
629 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
632 additionalProperties
=> 0,
634 node
=> get_standard_option
('pve-node'),
636 description
=> "Only list specific types of disks.",
638 enum
=> ['unused', 'journal_disks'],
648 dev
=> { type
=> 'string' },
649 used
=> { type
=> 'string', optional
=> 1 },
650 gpt
=> { type
=> 'boolean' },
651 size
=> { type
=> 'integer' },
652 osdid
=> { type
=> 'integer' },
653 vendor
=> { type
=> 'string', optional
=> 1 },
654 model
=> { type
=> 'string', optional
=> 1 },
655 serial
=> { type
=> 'string', optional
=> 1 },
658 # links => [ { rel => 'child', href => "{}" } ],
663 PVE
::CephTools
::check_ceph_inited
();
665 my $disks = PVE
::Diskmanage
::get_disks
(undef, 1);
668 foreach my $dev (keys %$disks) {
669 my $d = $disks->{$dev};
670 if ($param->{type
}) {
671 if ($param->{type
} eq 'journal_disks') {
672 next if $d->{osdid
} >= 0;
674 } elsif ($param->{type
} eq 'unused') {
677 die "internal error"; # should not happen
681 $d->{dev
} = "/dev/$dev";
688 __PACKAGE__-
>register_method ({
693 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
695 description
=> "Get Ceph configuration.",
697 additionalProperties
=> 0,
699 node
=> get_standard_option
('pve-node'),
702 returns
=> { type
=> 'string' },
706 PVE
::CephTools
::check_ceph_inited
();
708 my $path = PVE
::CephTools
::get_config
('pve_ceph_cfgpath');
709 return file_get_contents
($path);
713 my $add_storage = sub {
714 my ($pool, $storeid) = @_;
716 my $storage_params = {
721 content
=> 'rootdir,images',
724 PVE
::API2
::Storage
::Config-
>create($storage_params);
727 my $get_storages = sub {
730 my $cfg = PVE
::Storage
::config
();
732 my $storages = $cfg->{ids
};
734 foreach my $storeid (keys %$storages) {
735 my $curr = $storages->{$storeid};
736 $res->{$storeid} = $storages->{$storeid}
737 if $curr->{type
} eq 'rbd' && $pool eq $curr->{pool
};
743 __PACKAGE__-
>register_method ({
747 description
=> "Get Ceph monitor list.",
751 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
754 additionalProperties
=> 0,
756 node
=> get_standard_option
('pve-node'),
764 name
=> { type
=> 'string' },
765 addr
=> { type
=> 'string' },
768 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
773 PVE
::CephTools
::check_ceph_inited
();
777 my $cfg = PVE
::CephTools
::parse_ceph_config
();
780 foreach my $section (keys %$cfg) {
781 my $d = $cfg->{$section};
782 if ($section =~ m/^mon\.(\S+)$/) {
784 if ($d->{'mon addr'} && $d->{'host'}) {
785 $monhash->{$monid} = {
786 addr
=> $d->{'mon addr'},
787 host
=> $d->{'host'},
795 my $rados = PVE
::RADOS-
>new();
796 my $monstat = $rados->mon_command({ prefix
=> 'mon_status' });
797 my $mons = $monstat->{monmap
}->{mons
};
798 foreach my $d (@$mons) {
799 next if !defined($d->{name
});
800 $monhash->{$d->{name
}}->{rank
} = $d->{rank
};
801 $monhash->{$d->{name
}}->{addr
} = $d->{addr
};
802 if (grep { $_ eq $d->{rank
} } @{$monstat->{quorum
}}) {
803 $monhash->{$d->{name
}}->{quorum
} = 1;
809 return PVE
::RESTHandler
::hash_to_array
($monhash, 'name');
812 __PACKAGE__-
>register_method ({
816 description
=> "Create initial ceph default configuration and setup symlinks.",
820 check
=> ['perm', '/', [ 'Sys.Modify' ]],
823 additionalProperties
=> 0,
825 node
=> get_standard_option
('pve-node'),
827 description
=> "Use specific network for all ceph related traffic",
828 type
=> 'string', format
=> 'CIDR',
833 description
=> 'Targeted number of replicas per object',
841 description
=> 'Minimum number of available replicas per object to allow I/O',
849 description
=> "Placement group bits, used to specify the " .
850 "default number of placement groups.\n\nNOTE: 'osd pool " .
851 "default pg num' does not work for default pools.",
859 description
=> "Disable cephx authentification.\n\n" .
860 "WARNING: cephx is a security feature protecting against " .
861 "man-in-the-middle attacks. Only consider disabling cephx ".
862 "if your network is private!",
869 returns
=> { type
=> 'null' },
873 my $version = PVE
::CephTools
::get_local_version
(1);
875 if (!$version || $version < 12) {
876 die "Ceph Luminous required - please run 'pveceph install'\n";
878 PVE
::CephTools
::check_ceph_installed
('ceph_bin');
881 # simply load old config if it already exists
882 my $cfg = PVE
::CephTools
::parse_ceph_config
();
884 if (!$cfg->{global
}) {
889 UUID
::generate
($uuid);
890 UUID
::unparse
($uuid, $fsid);
892 my $auth = $param->{disable_cephx
} ?
'none' : 'cephx';
896 'auth cluster required' => $auth,
897 'auth service required' => $auth,
898 'auth client required' => $auth,
899 'osd journal size' => $pve_osd_default_journal_size,
900 'osd pool default size' => $param->{size
} // 3,
901 'osd pool default min size' => $param->{min_size
} // 2,
902 'mon allow pool delete' => 'true',
905 # this does not work for default pools
906 #'osd pool default pg num' => $pg_num,
907 #'osd pool default pgp num' => $pg_num,
910 $cfg->{global
}->{keyring
} = '/etc/pve/priv/$cluster.$name.keyring';
911 $cfg->{osd
}->{keyring
} = '/var/lib/ceph/osd/ceph-$id/keyring';
913 if ($param->{pg_bits
}) {
914 $cfg->{global
}->{'osd pg bits'} = $param->{pg_bits
};
915 $cfg->{global
}->{'osd pgp bits'} = $param->{pg_bits
};
918 if ($param->{network
}) {
919 $cfg->{global
}->{'public network'} = $param->{network
};
920 $cfg->{global
}->{'cluster network'} = $param->{network
};
923 PVE
::CephTools
::write_ceph_config
($cfg);
925 PVE
::CephTools
::setup_pve_symlinks
();
930 my $find_mon_ip = sub {
931 my ($pubnet, $node, $overwrite_ip) = @_;
934 return $overwrite_ip // PVE
::Cluster
::remote_node_ip
($node);
937 my $allowed_ips = PVE
::Network
::get_local_ip_from_cidr
($pubnet);
938 die "No IP configured and up from ceph public network '$pubnet'\n"
939 if scalar(@$allowed_ips) < 1;
941 if (!$overwrite_ip) {
942 if (scalar(@$allowed_ips) == 1) {
943 return $allowed_ips->[0];
945 die "Multiple IPs for ceph public network '$pubnet' detected on $node:\n".
946 join("\n", @$allowed_ips) ."\nuse 'mon-address' to specify one of them.\n";
948 if (grep { $_ eq $overwrite_ip } @$allowed_ips) {
949 return $overwrite_ip;
951 die "Monitor IP '$overwrite_ip' not in ceph public network '$pubnet'\n"
952 if !PVE
::Network
::is_ip_in_cidr
($overwrite_ip, $pubnet);
954 die "Specified monitor IP '$overwrite_ip' not configured or up on $node!\n";
958 my $create_mgr = sub {
959 my ($rados, $id) = @_;
961 my $clustername = PVE
::CephTools
::get_config
('ccname');
962 my $mgrdir = "/var/lib/ceph/mgr/$clustername-$id";
963 my $mgrkeyring = "$mgrdir/keyring";
964 my $mgrname = "mgr.$id";
966 die "ceph manager directory '$mgrdir' already exists\n"
969 print "creating manager directory '$mgrdir'\n";
971 print "creating keys for '$mgrname'\n";
972 my $output = $rados->mon_command({ prefix
=> 'auth get-or-create',
975 mon
=> 'allow profile mgr',
980 file_set_contents
($mgrkeyring, $output);
982 print "setting owner for directory\n";
983 run_command
(["chown", 'ceph:ceph', '-R', $mgrdir]);
985 print "enabling service 'ceph-mgr\@$id.service'\n";
986 PVE
::CephTools
::ceph_service_cmd
('enable', $mgrname);
987 print "starting service 'ceph-mgr\@$id.service'\n";
988 PVE
::CephTools
::ceph_service_cmd
('start', $mgrname);
991 my $destroy_mgr = sub {
994 my $clustername = PVE
::CephTools
::get_config
('ccname');
995 my $mgrname = "mgr.$mgrid";
996 my $mgrdir = "/var/lib/ceph/mgr/$clustername-$mgrid";
998 die "ceph manager directory '$mgrdir' not found\n"
1001 print "disabling service 'ceph-mgr\@$mgrid.service'\n";
1002 PVE
::CephTools
::ceph_service_cmd
('disable', $mgrname);
1003 print "stopping service 'ceph-mgr\@$mgrid.service'\n";
1004 PVE
::CephTools
::ceph_service_cmd
('stop', $mgrname);
1006 print "removing manager directory '$mgrdir'\n";
1007 File
::Path
::remove_tree
($mgrdir);
1010 __PACKAGE__-
>register_method ({
1011 name
=> 'createmon',
1014 description
=> "Create Ceph Monitor and Manager",
1018 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1021 additionalProperties
=> 0,
1023 node
=> get_standard_option
('pve-node'),
1027 pattern
=> '[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?',
1028 description
=> "The ID for the monitor, when omitted the same as the nodename",
1030 'exclude-manager' => {
1034 description
=> "When set, only a monitor will be created.",
1037 description
=> 'Overwrites autodetected monitor IP address. ' .
1038 'Must be in the public network of ceph.',
1039 type
=> 'string', format
=> 'ip',
1044 returns
=> { type
=> 'string' },
1048 PVE
::CephTools
::check_ceph_installed
('ceph_mon');
1050 PVE
::CephTools
::check_ceph_installed
('ceph_mgr')
1051 if (!$param->{'exclude-manager'});
1053 PVE
::CephTools
::check_ceph_inited
();
1055 PVE
::CephTools
::setup_pve_symlinks
();
1057 my $rpcenv = PVE
::RPCEnvironment
::get
();
1059 my $authuser = $rpcenv->get_user();
1061 my $cfg = PVE
::CephTools
::parse_ceph_config
();
1065 my $monaddrhash = {};
1067 my $systemd_managed = PVE
::CephTools
::systemd_managed
();
1069 foreach my $section (keys %$cfg) {
1070 next if $section eq 'global';
1071 my $d = $cfg->{$section};
1072 if ($section =~ m/^mon\./) {
1074 if ($d->{'mon addr'}) {
1075 $monaddrhash->{$d->{'mon addr'}} = $section;
1080 my $monid = $param->{id
} // $param->{node
};
1082 my $monsection = "mon.$monid";
1083 my $pubnet = $cfg->{global
}->{'public network'};
1084 my $ip = $find_mon_ip->($pubnet, $param->{node
}, $param->{'mon-address'});
1086 my $monaddr = Net
::IP
::ip_is_ipv6
($ip) ?
"[$ip]:6789" : "$ip:6789";
1087 my $monname = $param->{node
};
1089 die "monitor '$monsection' already exists\n" if $cfg->{$monsection};
1090 die "monitor address '$monaddr' already in use by '$monaddrhash->{$monaddr}'\n"
1091 if $monaddrhash->{$monaddr};
1096 my $pve_ckeyring_path = PVE
::CephTools
::get_config
('pve_ckeyring_path');
1098 if (! -f
$pve_ckeyring_path) {
1099 run_command
("ceph-authtool $pve_ckeyring_path --create-keyring " .
1100 "--gen-key -n client.admin");
1103 my $pve_mon_key_path = PVE
::CephTools
::get_config
('pve_mon_key_path');
1104 if (! -f
$pve_mon_key_path) {
1105 run_command
("cp $pve_ckeyring_path $pve_mon_key_path.tmp");
1106 run_command
("ceph-authtool $pve_mon_key_path.tmp -n client.admin --set-uid=0 " .
1107 "--cap mds 'allow' " .
1108 "--cap osd 'allow *' " .
1109 "--cap mgr 'allow *' " .
1110 "--cap mon 'allow *'");
1111 run_command
("cp $pve_mon_key_path.tmp /etc/ceph/ceph.client.admin.keyring") if $systemd_managed;
1112 run_command
("chown ceph:ceph /etc/ceph/ceph.client.admin.keyring") if $systemd_managed;
1113 run_command
("ceph-authtool $pve_mon_key_path.tmp --gen-key -n mon. --cap mon 'allow *'");
1114 run_command
("mv $pve_mon_key_path.tmp $pve_mon_key_path");
1117 my $ccname = PVE
::CephTools
::get_config
('ccname');
1119 my $mondir = "/var/lib/ceph/mon/$ccname-$monid";
1120 -d
$mondir && die "monitor filesystem '$mondir' already exist\n";
1122 my $monmap = "/tmp/monmap";
1127 run_command
("chown ceph:ceph $mondir") if $systemd_managed;
1129 if ($moncount > 0) {
1130 my $rados = PVE
::RADOS-
>new(timeout
=> PVE
::CephTools
::get_config
('long_rados_timeout'));
1131 my $mapdata = $rados->mon_command({ prefix
=> 'mon getmap', format
=> 'plain' });
1132 file_set_contents
($monmap, $mapdata);
1134 run_command
("monmaptool --create --clobber --add $monid $monaddr --print $monmap");
1137 run_command
("ceph-mon --mkfs -i $monid --monmap $monmap --keyring $pve_mon_key_path");
1138 run_command
("chown ceph:ceph -R $mondir") if $systemd_managed;
1143 File
::Path
::remove_tree
($mondir);
1147 $cfg->{$monsection} = {
1149 'mon addr' => $monaddr,
1152 PVE
::CephTools
::write_ceph_config
($cfg);
1154 my $create_keys_pid = fork();
1155 if (!defined($create_keys_pid)) {
1156 die "Could not spawn ceph-create-keys to create bootstrap keys\n";
1157 } elsif ($create_keys_pid == 0) {
1158 exit PVE
::Tools
::run_command
(['ceph-create-keys', '-i', $monid]);
1160 PVE
::CephTools
::ceph_service_cmd
('start', $monsection);
1162 if ($systemd_managed) {
1163 #to ensure we have the correct startup order.
1164 eval { PVE
::Tools
::run_command
(['/bin/systemctl', 'enable', "ceph-mon\@${monid}.service"]); };
1165 warn "Enable ceph-mon\@${monid}.service manually"if $@;
1167 waitpid($create_keys_pid, 0);
1171 if (!$param->{'exclude-manager'}) {
1172 my $rados = PVE
::RADOS-
>new(timeout
=> PVE
::CephTools
::get_config
('long_rados_timeout'));
1173 $create_mgr->($rados, $monid);
1177 return $rpcenv->fork_worker('cephcreatemon', $monsection, $authuser, $worker);
1180 __PACKAGE__-
>register_method ({
1181 name
=> 'destroymon',
1182 path
=> 'mon/{monid}',
1184 description
=> "Destroy Ceph Monitor and Manager.",
1188 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1191 additionalProperties
=> 0,
1193 node
=> get_standard_option
('pve-node'),
1195 description
=> 'Monitor ID',
1197 pattern
=> '[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?',
1199 'exclude-manager' => {
1203 description
=> "When set, removes only the monitor, not the manager"
1207 returns
=> { type
=> 'string' },
1211 my $rpcenv = PVE
::RPCEnvironment
::get
();
1213 my $authuser = $rpcenv->get_user();
1215 PVE
::CephTools
::check_ceph_inited
();
1217 my $cfg = PVE
::CephTools
::parse_ceph_config
();
1219 my $monid = $param->{monid
};
1220 my $monsection = "mon.$monid";
1222 my $rados = PVE
::RADOS-
>new();
1223 my $monstat = $rados->mon_command({ prefix
=> 'mon_status' });
1224 my $monlist = $monstat->{monmap
}->{mons
};
1226 die "no such monitor id '$monid'\n"
1227 if !defined($cfg->{$monsection});
1229 my $ccname = PVE
::CephTools
::get_config
('ccname');
1231 my $mondir = "/var/lib/ceph/mon/$ccname-$monid";
1232 -d
$mondir || die "monitor filesystem '$mondir' does not exist on this node\n";
1234 die "can't remove last monitor\n" if scalar(@$monlist) <= 1;
1239 # reopen with longer timeout
1240 $rados = PVE
::RADOS-
>new(timeout
=> PVE
::CephTools
::get_config
('long_rados_timeout'));
1242 $rados->mon_command({ prefix
=> "mon remove", name
=> $monid, format
=> 'plain' });
1244 eval { PVE
::CephTools
::ceph_service_cmd
('stop', $monsection); };
1247 delete $cfg->{$monsection};
1248 PVE
::CephTools
::write_ceph_config
($cfg);
1249 File
::Path
::remove_tree
($mondir);
1252 if (!$param->{'exclude-manager'}) {
1253 eval { $destroy_mgr->($monid); };
1258 return $rpcenv->fork_worker('cephdestroymon', $monsection, $authuser, $worker);
1261 __PACKAGE__-
>register_method ({
1262 name
=> 'createmgr',
1265 description
=> "Create Ceph Manager",
1269 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1272 additionalProperties
=> 0,
1274 node
=> get_standard_option
('pve-node'),
1278 pattern
=> '[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?',
1279 description
=> "The ID for the manager, when omitted the same as the nodename",
1283 returns
=> { type
=> 'string' },
1287 PVE
::CephTools
::check_ceph_installed
('ceph_mgr');
1289 PVE
::CephTools
::check_ceph_inited
();
1291 my $rpcenv = PVE
::RPCEnvironment
::get
();
1293 my $authuser = $rpcenv->get_user();
1295 my $mgrid = $param->{id
} // $param->{node
};
1300 my $rados = PVE
::RADOS-
>new(timeout
=> PVE
::CephTools
::get_config
('long_rados_timeout'));
1302 $create_mgr->($rados, $mgrid);
1305 return $rpcenv->fork_worker('cephcreatemgr', "mgr.$mgrid", $authuser, $worker);
1308 __PACKAGE__-
>register_method ({
1309 name
=> 'destroymgr',
1312 description
=> "Destroy Ceph Manager.",
1316 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1319 additionalProperties
=> 0,
1321 node
=> get_standard_option
('pve-node'),
1323 description
=> 'The ID of the manager',
1325 pattern
=> '[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?',
1329 returns
=> { type
=> 'string' },
1333 my $rpcenv = PVE
::RPCEnvironment
::get
();
1335 my $authuser = $rpcenv->get_user();
1337 PVE
::CephTools
::check_ceph_inited
();
1339 my $mgrid = $param->{id
};
1344 $destroy_mgr->($mgrid);
1347 return $rpcenv->fork_worker('cephdestroymgr', "mgr.$mgrid", $authuser, $worker);
1350 __PACKAGE__-
>register_method ({
1354 description
=> "Stop ceph services.",
1358 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1361 additionalProperties
=> 0,
1363 node
=> get_standard_option
('pve-node'),
1365 description
=> 'Ceph service name.',
1368 default => 'ceph.target',
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 default => 'ceph.target',
1420 pattern
=> '(mon|mds|osd|mgr)\.[A-Za-z0-9\-]{1,32}',
1424 returns
=> { type
=> 'string' },
1428 my $rpcenv = PVE
::RPCEnvironment
::get
();
1430 my $authuser = $rpcenv->get_user();
1432 PVE
::CephTools
::check_ceph_inited
();
1434 my $cfg = PVE
::CephTools
::parse_ceph_config
();
1435 scalar(keys %$cfg) || die "no configuration\n";
1440 my $cmd = ['start'];
1441 if ($param->{service
}) {
1442 push @$cmd, $param->{service
};
1445 PVE
::CephTools
::ceph_service_cmd
(@$cmd);
1448 return $rpcenv->fork_worker('srvstart', $param->{service
} || 'ceph',
1449 $authuser, $worker);
1452 __PACKAGE__-
>register_method ({
1456 description
=> "Restart ceph services.",
1460 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1463 additionalProperties
=> 0,
1465 node
=> get_standard_option
('pve-node'),
1467 description
=> 'Ceph service name.',
1470 default => 'ceph.target',
1471 pattern
=> '(mon|mds|osd|mgr)\.[A-Za-z0-9\-]{1,32}',
1475 returns
=> { type
=> 'string' },
1479 my $rpcenv = PVE
::RPCEnvironment
::get
();
1481 my $authuser = $rpcenv->get_user();
1483 PVE
::CephTools
::check_ceph_inited
();
1485 my $cfg = PVE
::CephTools
::parse_ceph_config
();
1486 scalar(keys %$cfg) || die "no configuration\n";
1491 my $cmd = ['restart'];
1492 if ($param->{service
}) {
1493 push @$cmd, $param->{service
};
1496 PVE
::CephTools
::ceph_service_cmd
(@$cmd);
1499 return $rpcenv->fork_worker('srvrestart', $param->{service
} || 'ceph',
1500 $authuser, $worker);
1503 __PACKAGE__-
>register_method ({
1507 description
=> "Get ceph status.",
1511 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
1514 additionalProperties
=> 0,
1516 node
=> get_standard_option
('pve-node'),
1519 returns
=> { type
=> 'object' },
1523 PVE
::CephTools
::check_ceph_enabled
();
1525 my $rados = PVE
::RADOS-
>new();
1526 my $status = $rados->mon_command({ prefix
=> 'status' });
1527 $status->{health
} = $rados->mon_command({ prefix
=> 'health', detail
=> 'detail' });
1531 __PACKAGE__-
>register_method ({
1535 description
=> "List all pools.",
1539 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
1542 additionalProperties
=> 0,
1544 node
=> get_standard_option
('pve-node'),
1552 pool
=> { type
=> 'integer' },
1553 pool_name
=> { type
=> 'string' },
1554 size
=> { type
=> 'integer' },
1557 links
=> [ { rel
=> 'child', href
=> "{pool_name}" } ],
1562 PVE
::CephTools
::check_ceph_inited
();
1564 my $rados = PVE
::RADOS-
>new();
1567 my $res = $rados->mon_command({ prefix
=> 'df' });
1569 foreach my $d (@{$res->{pools
}}) {
1570 next if !$d->{stats
};
1571 next if !defined($d->{id
});
1572 $stats->{$d->{id
}} = $d->{stats
};
1575 $res = $rados->mon_command({ prefix
=> 'osd dump' });
1576 my $rulestmp = $rados->mon_command({ prefix
=> 'osd crush rule dump'});
1579 for my $rule (@$rulestmp) {
1580 $rules->{$rule->{rule_id
}} = $rule->{rule_name
};
1584 foreach my $e (@{$res->{pools
}}) {
1586 foreach my $attr (qw(pool pool_name size min_size pg_num crush_rule)) {
1587 $d->{$attr} = $e->{$attr} if defined($e->{$attr});
1590 if (defined($d->{crush_rule
}) && defined($rules->{$d->{crush_rule
}})) {
1591 $d->{crush_rule_name
} = $rules->{$d->{crush_rule
}};
1594 if (my $s = $stats->{$d->{pool
}}) {
1595 $d->{bytes_used
} = $s->{bytes_used
};
1596 $d->{percent_used
} = $s->{percent_used
};
1605 __PACKAGE__-
>register_method ({
1606 name
=> 'createpool',
1609 description
=> "Create POOL",
1613 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1616 additionalProperties
=> 0,
1618 node
=> get_standard_option
('pve-node'),
1620 description
=> "The name of the pool. It must be unique.",
1624 description
=> 'Number of replicas per object',
1632 description
=> 'Minimum number of replicas per object',
1640 description
=> "Number of placement groups.",
1648 description
=> "The rule to use for mapping object placement in the cluster.",
1653 description
=> "The application of the pool, 'rbd' by default.",
1655 enum
=> ['rbd', 'cephfs', 'rgw'],
1659 description
=> "Configure VM and CT storage using the new pool.",
1665 returns
=> { type
=> 'string' },
1669 PVE
::Cluster
::check_cfs_quorum
();
1670 PVE
::CephTools
::check_ceph_inited
();
1672 my $pve_ckeyring_path = PVE
::CephTools
::get_config
('pve_ckeyring_path');
1674 die "not fully configured - missing '$pve_ckeyring_path'\n"
1675 if ! -f
$pve_ckeyring_path;
1677 my $pool = $param->{name
};
1678 my $rpcenv = PVE
::RPCEnvironment
::get
();
1679 my $user = $rpcenv->get_user();
1681 if ($param->{add_storages
}) {
1682 $rpcenv->check($user, '/storage', ['Datastore.Allocate']);
1683 die "pool name contains characters which are illegal for storage naming\n"
1684 if !PVE
::JSONSchema
::parse_storage_id
($pool);
1687 my $pg_num = $param->{pg_num
} || 64;
1688 my $size = $param->{size
} || 3;
1689 my $min_size = $param->{min_size
} || 2;
1690 my $application = $param->{application
} // 'rbd';
1694 PVE
::CephTools
::create_pool
($pool, $param);
1696 if ($param->{add_storages
}) {
1698 eval { $add_storage->($pool, "${pool}"); };
1700 warn "failed to add storage: $@";
1703 die "adding storage for pool '$pool' failed, check log and add manually!\n"
1708 return $rpcenv->fork_worker('cephcreatepool', $pool, $user, $worker);
1711 __PACKAGE__-
>register_method ({
1712 name
=> 'get_flags',
1715 description
=> "get all set ceph flags",
1719 check
=> ['perm', '/', [ 'Sys.Audit' ]],
1722 additionalProperties
=> 0,
1724 node
=> get_standard_option
('pve-node'),
1727 returns
=> { type
=> 'string' },
1731 PVE
::CephTools
::check_ceph_inited
();
1733 my $pve_ckeyring_path = PVE
::CephTools
::get_config
('pve_ckeyring_path');
1735 die "not fully configured - missing '$pve_ckeyring_path'\n"
1736 if ! -f
$pve_ckeyring_path;
1738 my $rados = PVE
::RADOS-
>new();
1740 my $stat = $rados->mon_command({ prefix
=> 'osd dump' });
1742 return $stat->{flags
} // '';
1745 __PACKAGE__-
>register_method ({
1747 path
=> 'flags/{flag}',
1749 description
=> "Set a ceph flag",
1753 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1756 additionalProperties
=> 0,
1758 node
=> get_standard_option
('pve-node'),
1760 description
=> 'The ceph flag to set/unset',
1762 enum
=> [ 'full', 'pause', 'noup', 'nodown', 'noout', 'noin', 'nobackfill', 'norebalance', 'norecover', 'noscrub', 'nodeep-scrub', 'notieragent'],
1766 returns
=> { type
=> 'null' },
1770 PVE
::CephTools
::check_ceph_inited
();
1772 my $pve_ckeyring_path = PVE
::CephTools
::get_config
('pve_ckeyring_path');
1774 die "not fully configured - missing '$pve_ckeyring_path'\n"
1775 if ! -f
$pve_ckeyring_path;
1777 my $set = $param->{set
} // !$param->{unset
};
1778 my $rados = PVE
::RADOS-
>new();
1780 $rados->mon_command({
1781 prefix
=> "osd set",
1782 key
=> $param->{flag
},
1788 __PACKAGE__-
>register_method ({
1789 name
=> 'unset_flag',
1790 path
=> 'flags/{flag}',
1792 description
=> "Unset a ceph flag",
1796 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1799 additionalProperties
=> 0,
1801 node
=> get_standard_option
('pve-node'),
1803 description
=> 'The ceph flag to set/unset',
1805 enum
=> [ 'full', 'pause', 'noup', 'nodown', 'noout', 'noin', 'nobackfill', 'norebalance', 'norecover', 'noscrub', 'nodeep-scrub', 'notieragent'],
1809 returns
=> { type
=> 'null' },
1813 PVE
::CephTools
::check_ceph_inited
();
1815 my $pve_ckeyring_path = PVE
::CephTools
::get_config
('pve_ckeyring_path');
1817 die "not fully configured - missing '$pve_ckeyring_path'\n"
1818 if ! -f
$pve_ckeyring_path;
1820 my $set = $param->{set
} // !$param->{unset
};
1821 my $rados = PVE
::RADOS-
>new();
1823 $rados->mon_command({
1824 prefix
=> "osd unset",
1825 key
=> $param->{flag
},
1831 __PACKAGE__-
>register_method ({
1832 name
=> 'destroypool',
1833 path
=> 'pools/{name}',
1835 description
=> "Destroy pool",
1839 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1842 additionalProperties
=> 0,
1844 node
=> get_standard_option
('pve-node'),
1846 description
=> "The name of the pool. It must be unique.",
1850 description
=> "If true, destroys pool even if in use",
1855 remove_storages
=> {
1856 description
=> "Remove all pveceph-managed storages configured for this pool",
1863 returns
=> { type
=> 'string' },
1867 PVE
::CephTools
::check_ceph_inited
();
1869 my $rpcenv = PVE
::RPCEnvironment
::get
();
1870 my $user = $rpcenv->get_user();
1871 $rpcenv->check($user, '/storage', ['Datastore.Allocate'])
1872 if $param->{remove_storages
};
1874 my $pool = $param->{name
};
1877 my $storages = $get_storages->($pool);
1879 # if not forced, destroy ceph pool only when no
1880 # vm disks are on it anymore
1881 if (!$param->{force
}) {
1882 my $storagecfg = PVE
::Storage
::config
();
1883 foreach my $storeid (keys %$storages) {
1884 my $storage = $storages->{$storeid};
1886 # check if any vm disks are on the pool
1887 print "checking storage '$storeid' for RBD images..\n";
1888 my $res = PVE
::Storage
::vdisk_list
($storagecfg, $storeid);
1889 die "ceph pool '$pool' still in use by storage '$storeid'\n"
1890 if @{$res->{$storeid}} != 0;
1894 PVE
::CephTools
::destroy_pool
($pool);
1896 if ($param->{remove_storages
}) {
1898 foreach my $storeid (keys %$storages) {
1899 # skip external clusters, not managed by pveceph
1900 next if $storages->{$storeid}->{monhost
};
1901 eval { PVE
::API2
::Storage
::Config-
>delete({storage
=> $storeid}) };
1903 warn "failed to remove storage '$storeid': $@\n";
1907 die "failed to remove (some) storages - check log and remove manually!\n"
1911 return $rpcenv->fork_worker('cephdestroypool', $pool, $user, $worker);
1915 __PACKAGE__-
>register_method ({
1919 description
=> "Get OSD crush map",
1923 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
1926 additionalProperties
=> 0,
1928 node
=> get_standard_option
('pve-node'),
1931 returns
=> { type
=> 'string' },
1935 PVE
::CephTools
::check_ceph_inited
();
1937 # this produces JSON (difficult to read for the user)
1938 # my $txt = &$run_ceph_cmd_text(['osd', 'crush', 'dump'], quiet => 1);
1942 my $mapfile = "/var/tmp/ceph-crush.map.$$";
1943 my $mapdata = "/var/tmp/ceph-crush.txt.$$";
1945 my $rados = PVE
::RADOS-
>new();
1948 my $bindata = $rados->mon_command({ prefix
=> 'osd getcrushmap', format
=> 'plain' });
1949 file_set_contents
($mapfile, $bindata);
1950 run_command
(['crushtool', '-d', $mapfile, '-o', $mapdata]);
1951 $txt = file_get_contents
($mapdata);
1963 __PACKAGE__-
>register_method({
1967 description
=> "Read ceph log",
1970 check
=> ['perm', '/nodes/{node}', [ 'Sys.Syslog' ]],
1974 additionalProperties
=> 0,
1976 node
=> get_standard_option
('pve-node'),
1995 description
=> "Line number",
1999 description
=> "Line text",
2008 my $rpcenv = PVE
::RPCEnvironment
::get
();
2009 my $user = $rpcenv->get_user();
2010 my $node = $param->{node
};
2012 my $logfile = "/var/log/ceph/ceph.log";
2013 my ($count, $lines) = PVE
::Tools
::dump_logfile
($logfile, $param->{start
}, $param->{limit
});
2015 $rpcenv->set_result_attrib('total', $count);
2020 __PACKAGE__-
>register_method ({
2024 description
=> "List ceph rules.",
2028 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
2031 additionalProperties
=> 0,
2033 node
=> get_standard_option
('pve-node'),
2042 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
2047 PVE
::CephTools
::check_ceph_inited
();
2049 my $rados = PVE
::RADOS-
>new();
2051 my $rules = $rados->mon_command({ prefix
=> 'osd crush rule ls' });
2055 foreach my $rule (@$rules) {
2056 push @$res, { name
=> $rule };