]>
git.proxmox.com Git - pve-manager.git/blob - PVE/API2/Ceph.pm
1 package PVE
::API2
::CephOSD
;
11 use PVE
::Cluster
qw(cfs_read_file cfs_write_file);
13 use PVE
::Exception
qw(raise_param_exc);
14 use PVE
::JSONSchema
qw(get_standard_option);
17 use PVE
::RPCEnvironment
;
18 use PVE
::Tools
qw(run_command file_set_contents);
20 use base
qw(PVE::RESTHandler);
22 my $get_osd_status = sub {
23 my ($rados, $osdid) = @_;
25 my $stat = $rados->mon_command({ prefix
=> 'osd dump' });
27 my $osdlist = $stat->{osds
} || [];
29 my $flags = $stat->{flags
} || undef;
32 foreach my $d (@$osdlist) {
33 $osdstat->{$d->{osd
}} = $d if defined($d->{osd
});
35 if (defined($osdid)) {
36 die "no such OSD '$osdid'\n" if !$osdstat->{$osdid};
37 return $osdstat->{$osdid};
40 return wantarray?
($osdstat, $flags):$osdstat;
43 my $get_osd_usage = sub {
46 my $osdlist = $rados->mon_command({ prefix
=> 'pg dump',
47 dumpcontents
=> [ 'osds' ]}) || [];
50 foreach my $d (@$osdlist) {
51 $osdstat->{$d->{osd
}} = $d if defined($d->{osd
});
57 __PACKAGE__-
>register_method ({
61 description
=> "Get Ceph osd list/tree.",
65 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
68 additionalProperties
=> 0,
70 node
=> get_standard_option
('pve-node'),
73 # fixme: return a list instead of extjs tree format ?
80 PVE
::Ceph
::Tools
::check_ceph_inited
();
82 my $rados = PVE
::RADOS-
>new();
83 my $res = $rados->mon_command({ prefix
=> 'osd tree' });
85 die "no tree nodes found\n" if !($res && $res->{nodes
});
87 my ($osdhash, $flags) = &$get_osd_status($rados);
89 my $usagehash = &$get_osd_usage($rados);
91 my $osdmetadata_tmp = $rados->mon_command({ prefix
=> 'osd metadata' });
94 foreach my $osd (@$osdmetadata_tmp) {
95 $osdmetadata->{$osd->{id
}} = $osd;
100 foreach my $e (@{$res->{nodes
}}) {
101 $nodes->{$e->{id
}} = $e;
109 foreach my $opt (qw(status crush_weight reweight device_class)) {
110 $new->{$opt} = $e->{$opt} if defined($e->{$opt});
113 if (my $stat = $osdhash->{$e->{id
}}) {
114 $new->{in} = $stat->{in} if defined($stat->{in});
117 if (my $stat = $usagehash->{$e->{id
}}) {
118 $new->{total_space
} = ($stat->{kb
} || 1) * 1024;
119 $new->{bytes_used
} = ($stat->{kb_used
} || 0) * 1024;
120 $new->{percent_used
} = ($new->{bytes_used
}*100)/$new->{total_space
};
121 if (my $d = $stat->{perf_stat
}) {
122 $new->{commit_latency_ms
} = $d->{commit_latency_ms
};
123 $new->{apply_latency_ms
} = $d->{apply_latency_ms
};
127 my $osdmd = $osdmetadata->{$e->{id
}};
128 if ($e->{type
} eq 'osd' && $osdmd) {
129 if ($osdmd->{bluefs
}) {
130 $new->{osdtype
} = 'bluestore';
131 $new->{blfsdev
} = $osdmd->{bluestore_bdev_dev_node
};
132 $new->{dbdev
} = $osdmd->{bluefs_db_dev_node
};
133 $new->{waldev
} = $osdmd->{bluefs_wal_dev_node
};
135 $new->{osdtype
} = 'filestore';
139 $newnodes->{$e->{id
}} = $new;
142 foreach my $e (@{$res->{nodes
}}) {
143 my $new = $newnodes->{$e->{id
}};
144 if ($e->{children
} && scalar(@{$e->{children
}})) {
145 $new->{children
} = [];
147 foreach my $cid (@{$e->{children
}}) {
148 $nodes->{$cid}->{parent
} = $e->{id
};
149 if ($nodes->{$cid}->{type
} eq 'osd' &&
150 $e->{type
} eq 'host') {
151 $newnodes->{$cid}->{host
} = $e->{name
};
153 push @{$new->{children
}}, $newnodes->{$cid};
156 $new->{leaf
} = ($e->{id
} >= 0) ?
1 : 0;
161 foreach my $e (@{$res->{nodes
}}) {
162 if (!$nodes->{$e->{id
}}->{parent
}) {
163 push @$roots, $newnodes->{$e->{id
}};
167 die "no root node\n" if !@$roots;
169 my $data = { root
=> { leaf
=> 0, children
=> $roots } };
171 # we want this for the noout flag
172 $data->{flags
} = $flags if $flags;
177 __PACKAGE__-
>register_method ({
181 description
=> "Create OSD",
185 additionalProperties
=> 0,
187 node
=> get_standard_option
('pve-node'),
189 description
=> "Block device name.",
193 description
=> "Block device name for journal (filestore) or block.db (bluestore).",
198 description
=> "Block device name for block.wal (bluestore only).",
203 description
=> "File system type (filestore only).",
205 enum
=> ['xfs', 'ext4'],
210 description
=> "Use bluestore instead of filestore. This is the default.",
217 returns
=> { type
=> 'string' },
221 my $rpcenv = PVE
::RPCEnvironment
::get
();
223 my $authuser = $rpcenv->get_user();
225 raise_param_exc
({ 'bluestore' => "conflicts with parameter 'fstype'" })
226 if (defined($param->{fstype
}) && defined($param->{bluestore
}) && $param->{bluestore
});
228 PVE
::Ceph
::Tools
::check_ceph_inited
();
230 PVE
::Ceph
::Tools
::setup_pve_symlinks
();
232 PVE
::Ceph
::Tools
::check_ceph_installed
('ceph_osd');
234 my $bluestore = $param->{bluestore
} // 1;
239 if ($param->{journal_dev
} && ($param->{journal_dev
} ne $param->{dev
})) {
240 $journal_dev = PVE
::Diskmanage
::verify_blockdev_path
($param->{journal_dev
});
243 if ($param->{wal_dev
} &&
244 ($param->{wal_dev
} ne $param->{dev
}) &&
245 (!$param->{journal_dev
} || $param->{wal_dev
} ne $param->{journal_dev
})) {
246 raise_param_exc
({ 'wal_dev' => "can only be set with paramater 'bluestore'"})
248 $wal_dev = PVE
::Diskmanage
::verify_blockdev_path
($param->{wal_dev
});
251 $param->{dev
} = PVE
::Diskmanage
::verify_blockdev_path
($param->{dev
});
253 my $devname = $param->{dev
};
254 $devname =~ s
|/dev/||;
256 my $disklist = PVE
::Diskmanage
::get_disks
($devname, 1);
258 my $diskinfo = $disklist->{$devname};
259 die "unable to get device info for '$devname'\n"
262 die "device '$param->{dev}' is in use\n"
263 if $diskinfo->{used
};
265 my $devpath = $diskinfo->{devpath
};
266 my $rados = PVE
::RADOS-
>new();
267 my $monstat = $rados->mon_command({ prefix
=> 'mon_status' });
268 die "unable to get fsid\n" if !$monstat->{monmap
} || !$monstat->{monmap
}->{fsid
};
270 my $fsid = $monstat->{monmap
}->{fsid
};
271 $fsid = $1 if $fsid =~ m/^([0-9a-f\-]+)$/;
273 my $ceph_bootstrap_osd_keyring = PVE
::Ceph
::Tools
::get_config
('ceph_bootstrap_osd_keyring');
275 if (! -f
$ceph_bootstrap_osd_keyring) {
276 my $bindata = $rados->mon_command({ prefix
=> 'auth get', entity
=> 'client.bootstrap-osd', format
=> 'plain' });
277 file_set_contents
($ceph_bootstrap_osd_keyring, $bindata);
283 my $fstype = $param->{fstype
} || 'xfs';
286 my $ccname = PVE
::Ceph
::Tools
::get_config
('ccname');
288 my $cmd = ['ceph-disk', 'prepare', '--zap-disk',
289 '--cluster', $ccname, '--cluster-uuid', $fsid ];
292 print "create OSD on $devpath (bluestore)\n";
293 push @$cmd, '--bluestore';
296 print "using device '$journal_dev' for block.db\n";
297 push @$cmd, '--block.db', $journal_dev;
301 print "using device '$wal_dev' for block.wal\n";
302 push @$cmd, '--block.wal', $wal_dev;
305 push @$cmd, $devpath;
307 print "create OSD on $devpath ($fstype)\n";
308 push @$cmd, '--filestore', '--fs-type', $fstype;
310 print "using device '$journal_dev' for journal\n";
311 push @$cmd, '--journal-dev', $devpath, $journal_dev;
313 push @$cmd, $devpath;
317 PVE
::Ceph
::Tools
::wipe_disks
($devpath);
322 return $rpcenv->fork_worker('cephcreateosd', $devname, $authuser, $worker);
325 __PACKAGE__-
>register_method ({
326 name
=> 'destroyosd',
329 description
=> "Destroy OSD",
333 additionalProperties
=> 0,
335 node
=> get_standard_option
('pve-node'),
337 description
=> 'OSD ID',
341 description
=> "If set, we remove partition table entries.",
348 returns
=> { type
=> 'string' },
352 my $rpcenv = PVE
::RPCEnvironment
::get
();
354 my $authuser = $rpcenv->get_user();
356 PVE
::Ceph
::Tools
::check_ceph_inited
();
358 my $osdid = $param->{osdid
};
360 my $rados = PVE
::RADOS-
>new();
361 my $osdstat = &$get_osd_status($rados, $osdid);
363 die "osd is in use (in == 1)\n" if $osdstat->{in};
364 #&$run_ceph_cmd(['osd', 'out', $osdid]);
366 die "osd is still runnung (up == 1)\n" if $osdstat->{up
};
368 my $osdsection = "osd.$osdid";
373 # reopen with longer timeout
374 $rados = PVE
::RADOS-
>new(timeout
=> PVE
::Ceph
::Tools
::get_config
('long_rados_timeout'));
376 print "destroy OSD $osdsection\n";
379 PVE
::Ceph
::Tools
::ceph_service_cmd
('stop', $osdsection);
380 PVE
::Ceph
::Tools
::ceph_service_cmd
('disable', $osdsection);
384 print "Remove $osdsection from the CRUSH map\n";
385 $rados->mon_command({ prefix
=> "osd crush remove", name
=> $osdsection, format
=> 'plain' });
387 print "Remove the $osdsection authentication key.\n";
388 $rados->mon_command({ prefix
=> "auth del", entity
=> $osdsection, format
=> 'plain' });
390 print "Remove OSD $osdsection\n";
391 $rados->mon_command({ prefix
=> "osd rm", ids
=> [ $osdsection ], format
=> 'plain' });
393 # try to unmount from standard mount point
394 my $mountpoint = "/var/lib/ceph/osd/ceph-$osdid";
396 my $disks_to_wipe = {};
397 my $remove_partition = sub {
400 return if !$part || (! -b
$part );
401 my $partnum = PVE
::Diskmanage
::get_partnum
($part);
402 my $devpath = PVE
::Diskmanage
::get_blockdev
($part);
404 print "remove partition $part (disk '${devpath}', partnum $partnum)\n";
405 eval { run_command
(['/sbin/sgdisk', '-d', $partnum, "${devpath}"]); };
408 $disks_to_wipe->{$devpath} = 1;
411 my $partitions_to_remove = [];
413 if ($param->{cleanup
}) {
414 if (my $fd = IO
::File-
>new("/proc/mounts", "r")) {
415 while (defined(my $line = <$fd>)) {
416 my ($dev, $path, $fstype) = split(/\s+/, $line);
417 next if !($dev && $path && $fstype);
418 next if $dev !~ m
|^/dev/|;
419 if ($path eq $mountpoint) {
420 my $data_part = abs_path
($dev);
421 push @$partitions_to_remove, $data_part;
428 foreach my $path (qw(journal block block.db block.wal)) {
429 my $part = abs_path
("$mountpoint/$path");
431 push @$partitions_to_remove, $part;
436 print "Unmount OSD $osdsection from $mountpoint\n";
437 eval { run_command
(['/bin/umount', $mountpoint]); };
440 } elsif ($param->{cleanup
}) {
441 #be aware of the ceph udev rules which can remount.
442 foreach my $part (@$partitions_to_remove) {
443 $remove_partition->($part);
446 PVE
::Ceph
::Tools
::wipe_disks
(keys %$disks_to_wipe);
450 return $rpcenv->fork_worker('cephdestroyosd', $osdsection, $authuser, $worker);
453 __PACKAGE__-
>register_method ({
455 path
=> '{osdid}/in',
457 description
=> "ceph osd in",
461 check
=> ['perm', '/', [ 'Sys.Modify' ]],
464 additionalProperties
=> 0,
466 node
=> get_standard_option
('pve-node'),
468 description
=> 'OSD ID',
473 returns
=> { type
=> "null" },
477 PVE
::Ceph
::Tools
::check_ceph_inited
();
479 my $osdid = $param->{osdid
};
481 my $rados = PVE
::RADOS-
>new();
483 my $osdstat = &$get_osd_status($rados, $osdid); # osd exists?
485 my $osdsection = "osd.$osdid";
487 $rados->mon_command({ prefix
=> "osd in", ids
=> [ $osdsection ], format
=> 'plain' });
492 __PACKAGE__-
>register_method ({
494 path
=> '{osdid}/out',
496 description
=> "ceph osd out",
500 check
=> ['perm', '/', [ 'Sys.Modify' ]],
503 additionalProperties
=> 0,
505 node
=> get_standard_option
('pve-node'),
507 description
=> 'OSD ID',
512 returns
=> { type
=> "null" },
516 PVE
::Ceph
::Tools
::check_ceph_inited
();
518 my $osdid = $param->{osdid
};
520 my $rados = PVE
::RADOS-
>new();
522 my $osdstat = &$get_osd_status($rados, $osdid); # osd exists?
524 my $osdsection = "osd.$osdid";
526 $rados->mon_command({ prefix
=> "osd out", ids
=> [ $osdsection ], format
=> 'plain' });
531 package PVE
::API2
::Ceph
;
540 use PVE
::Ceph
::Tools
;
541 use PVE
::Cluster
qw(cfs_read_file cfs_write_file);
542 use PVE
::JSONSchema
qw(get_standard_option);
545 use PVE
::RESTHandler
;
546 use PVE
::RPCEnvironment
;
548 use PVE
::Tools
qw(run_command file_get_contents file_set_contents);
550 use PVE
::API2
::Ceph
::FS
;
551 use PVE
::API2
::Ceph
::MDS
;
552 use PVE
::API2
::Storage
::Config
;
554 use base
qw(PVE::RESTHandler);
556 my $pve_osd_default_journal_size = 1024*5;
558 __PACKAGE__-
>register_method ({
559 subclass
=> "PVE::API2::CephOSD",
563 __PACKAGE__-
>register_method ({
564 subclass
=> "PVE::API2::Ceph::MDS",
568 __PACKAGE__-
>register_method ({
569 subclass
=> "PVE::API2::Ceph::FS",
573 __PACKAGE__-
>register_method ({
577 description
=> "Directory index.",
578 permissions
=> { user
=> 'all' },
580 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
583 additionalProperties
=> 0,
585 node
=> get_standard_option
('pve-node'),
594 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
608 { name
=> 'status' },
610 { name
=> 'config' },
620 __PACKAGE__-
>register_method ({
624 description
=> "List local disks.",
628 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
631 additionalProperties
=> 0,
633 node
=> get_standard_option
('pve-node'),
635 description
=> "Only list specific types of disks.",
637 enum
=> ['unused', 'journal_disks'],
647 dev
=> { type
=> 'string' },
648 used
=> { type
=> 'string', optional
=> 1 },
649 gpt
=> { type
=> 'boolean' },
650 size
=> { type
=> 'integer' },
651 osdid
=> { type
=> 'integer' },
652 vendor
=> { type
=> 'string', optional
=> 1 },
653 model
=> { type
=> 'string', optional
=> 1 },
654 serial
=> { type
=> 'string', optional
=> 1 },
657 # links => [ { rel => 'child', href => "{}" } ],
662 PVE
::Ceph
::Tools
::check_ceph_inited
();
664 my $disks = PVE
::Diskmanage
::get_disks
(undef, 1);
667 foreach my $dev (keys %$disks) {
668 my $d = $disks->{$dev};
669 if ($param->{type
}) {
670 if ($param->{type
} eq 'journal_disks') {
671 next if $d->{osdid
} >= 0;
673 } elsif ($param->{type
} eq 'unused') {
676 die "internal error"; # should not happen
680 $d->{dev
} = "/dev/$dev";
687 __PACKAGE__-
>register_method ({
692 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
694 description
=> "Get Ceph configuration.",
696 additionalProperties
=> 0,
698 node
=> get_standard_option
('pve-node'),
701 returns
=> { type
=> 'string' },
705 PVE
::Ceph
::Tools
::check_ceph_inited
();
707 my $path = PVE
::Ceph
::Tools
::get_config
('pve_ceph_cfgpath');
708 return file_get_contents
($path);
712 my $add_storage = sub {
713 my ($pool, $storeid) = @_;
715 my $storage_params = {
720 content
=> 'rootdir,images',
723 PVE
::API2
::Storage
::Config-
>create($storage_params);
726 my $get_storages = sub {
729 my $cfg = PVE
::Storage
::config
();
731 my $storages = $cfg->{ids
};
733 foreach my $storeid (keys %$storages) {
734 my $curr = $storages->{$storeid};
735 $res->{$storeid} = $storages->{$storeid}
736 if $curr->{type
} eq 'rbd' && $pool eq $curr->{pool
};
742 __PACKAGE__-
>register_method ({
746 description
=> "Get Ceph monitor list.",
750 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
753 additionalProperties
=> 0,
755 node
=> get_standard_option
('pve-node'),
763 name
=> { type
=> 'string' },
764 addr
=> { type
=> 'string' },
767 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
772 PVE
::Ceph
::Tools
::check_ceph_inited
();
776 my $cfg = cfs_read_file
('ceph.conf');
779 foreach my $section (keys %$cfg) {
780 my $d = $cfg->{$section};
781 if ($section =~ m/^mon\.(\S+)$/) {
783 if ($d->{'mon addr'} && $d->{'host'}) {
784 $monhash->{$monid} = {
785 addr
=> $d->{'mon addr'},
786 host
=> $d->{'host'},
794 my $rados = PVE
::RADOS-
>new();
795 my $monstat = $rados->mon_command({ prefix
=> 'mon_status' });
796 my $mons = $monstat->{monmap
}->{mons
};
797 foreach my $d (@$mons) {
798 next if !defined($d->{name
});
799 $monhash->{$d->{name
}}->{rank
} = $d->{rank
};
800 $monhash->{$d->{name
}}->{addr
} = $d->{addr
};
801 if (grep { $_ eq $d->{rank
} } @{$monstat->{quorum
}}) {
802 $monhash->{$d->{name
}}->{quorum
} = 1;
808 return PVE
::RESTHandler
::hash_to_array
($monhash, 'name');
811 __PACKAGE__-
>register_method ({
815 description
=> "Create initial ceph default configuration and setup symlinks.",
819 check
=> ['perm', '/', [ 'Sys.Modify' ]],
822 additionalProperties
=> 0,
824 node
=> get_standard_option
('pve-node'),
826 description
=> "Use specific network for all ceph related traffic",
827 type
=> 'string', format
=> 'CIDR',
831 'cluster-network' => {
832 description
=> "Declare a separate cluster network, OSDs will route" .
833 "heartbeat, object replication and recovery traffic over it",
834 type
=> 'string', format
=> 'CIDR',
835 requires
=> 'network',
840 description
=> 'Targeted number of replicas per object',
848 description
=> 'Minimum number of available replicas per object to allow I/O',
856 description
=> "Placement group bits, used to specify the " .
857 "default number of placement groups.\n\nNOTE: 'osd pool " .
858 "default pg num' does not work for default pools.",
866 description
=> "Disable cephx authentification.\n\n" .
867 "WARNING: cephx is a security feature protecting against " .
868 "man-in-the-middle attacks. Only consider disabling cephx ".
869 "if your network is private!",
876 returns
=> { type
=> 'null' },
880 my $version = PVE
::Ceph
::Tools
::get_local_version
(1);
882 if (!$version || $version < 12) {
883 die "Ceph Luminous required - please run 'pveceph install'\n";
885 PVE
::Ceph
::Tools
::check_ceph_installed
('ceph_bin');
888 # simply load old config if it already exists
889 my $cfg = cfs_read_file
('ceph.conf');
891 if (!$cfg->{global
}) {
896 UUID
::generate
($uuid);
897 UUID
::unparse
($uuid, $fsid);
899 my $auth = $param->{disable_cephx
} ?
'none' : 'cephx';
903 'auth cluster required' => $auth,
904 'auth service required' => $auth,
905 'auth client required' => $auth,
906 'osd journal size' => $pve_osd_default_journal_size,
907 'osd pool default size' => $param->{size
} // 3,
908 'osd pool default min size' => $param->{min_size
} // 2,
909 'mon allow pool delete' => 'true',
912 # this does not work for default pools
913 #'osd pool default pg num' => $pg_num,
914 #'osd pool default pgp num' => $pg_num,
917 $cfg->{global
}->{keyring
} = '/etc/pve/priv/$cluster.$name.keyring';
918 $cfg->{osd
}->{keyring
} = '/var/lib/ceph/osd/ceph-$id/keyring';
920 if ($param->{pg_bits
}) {
921 $cfg->{global
}->{'osd pg bits'} = $param->{pg_bits
};
922 $cfg->{global
}->{'osd pgp bits'} = $param->{pg_bits
};
925 if ($param->{network
}) {
926 $cfg->{global
}->{'public network'} = $param->{network
};
927 $cfg->{global
}->{'cluster network'} = $param->{network
};
930 if ($param->{'cluster-network'}) {
931 $cfg->{global
}->{'cluster network'} = $param->{'cluster-network'};
934 cfs_write_file
('ceph.conf', $cfg);
936 PVE
::Ceph
::Tools
::setup_pve_symlinks
();
941 my $find_mon_ip = sub {
942 my ($pubnet, $node, $overwrite_ip) = @_;
945 return $overwrite_ip // PVE
::Cluster
::remote_node_ip
($node);
948 my $allowed_ips = PVE
::Network
::get_local_ip_from_cidr
($pubnet);
949 die "No IP configured and up from ceph public network '$pubnet'\n"
950 if scalar(@$allowed_ips) < 1;
952 if (!$overwrite_ip) {
953 if (scalar(@$allowed_ips) == 1) {
954 return $allowed_ips->[0];
956 die "Multiple IPs for ceph public network '$pubnet' detected on $node:\n".
957 join("\n", @$allowed_ips) ."\nuse 'mon-address' to specify one of them.\n";
959 if (grep { $_ eq $overwrite_ip } @$allowed_ips) {
960 return $overwrite_ip;
962 die "Monitor IP '$overwrite_ip' not in ceph public network '$pubnet'\n"
963 if !PVE
::Network
::is_ip_in_cidr
($overwrite_ip, $pubnet);
965 die "Specified monitor IP '$overwrite_ip' not configured or up on $node!\n";
969 my $create_mgr = sub {
970 my ($rados, $id) = @_;
972 my $clustername = PVE
::Ceph
::Tools
::get_config
('ccname');
973 my $mgrdir = "/var/lib/ceph/mgr/$clustername-$id";
974 my $mgrkeyring = "$mgrdir/keyring";
975 my $mgrname = "mgr.$id";
977 die "ceph manager directory '$mgrdir' already exists\n"
980 print "creating manager directory '$mgrdir'\n";
982 print "creating keys for '$mgrname'\n";
983 my $output = $rados->mon_command({ prefix
=> 'auth get-or-create',
986 mon
=> 'allow profile mgr',
991 file_set_contents
($mgrkeyring, $output);
993 print "setting owner for directory\n";
994 run_command
(["chown", 'ceph:ceph', '-R', $mgrdir]);
996 print "enabling service 'ceph-mgr\@$id.service'\n";
997 PVE
::Ceph
::Tools
::ceph_service_cmd
('enable', $mgrname);
998 print "starting service 'ceph-mgr\@$id.service'\n";
999 PVE
::Ceph
::Tools
::ceph_service_cmd
('start', $mgrname);
1002 my $destroy_mgr = sub {
1005 my $clustername = PVE
::Ceph
::Tools
::get_config
('ccname');
1006 my $mgrname = "mgr.$mgrid";
1007 my $mgrdir = "/var/lib/ceph/mgr/$clustername-$mgrid";
1009 die "ceph manager directory '$mgrdir' not found\n"
1012 print "disabling service 'ceph-mgr\@$mgrid.service'\n";
1013 PVE
::Ceph
::Tools
::ceph_service_cmd
('disable', $mgrname);
1014 print "stopping service 'ceph-mgr\@$mgrid.service'\n";
1015 PVE
::Ceph
::Tools
::ceph_service_cmd
('stop', $mgrname);
1017 print "removing manager directory '$mgrdir'\n";
1018 File
::Path
::remove_tree
($mgrdir);
1021 __PACKAGE__-
>register_method ({
1022 name
=> 'createmon',
1025 description
=> "Create Ceph Monitor and Manager",
1029 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1032 additionalProperties
=> 0,
1034 node
=> get_standard_option
('pve-node'),
1038 pattern
=> '[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?',
1039 description
=> "The ID for the monitor, when omitted the same as the nodename",
1041 'exclude-manager' => {
1045 description
=> "When set, only a monitor will be created.",
1048 description
=> 'Overwrites autodetected monitor IP address. ' .
1049 'Must be in the public network of ceph.',
1050 type
=> 'string', format
=> 'ip',
1055 returns
=> { type
=> 'string' },
1059 PVE
::Ceph
::Tools
::check_ceph_installed
('ceph_mon');
1061 PVE
::Ceph
::Tools
::check_ceph_installed
('ceph_mgr')
1062 if (!$param->{'exclude-manager'});
1064 PVE
::Ceph
::Tools
::check_ceph_inited
();
1066 PVE
::Ceph
::Tools
::setup_pve_symlinks
();
1068 my $rpcenv = PVE
::RPCEnvironment
::get
();
1070 my $authuser = $rpcenv->get_user();
1072 my $cfg = cfs_read_file
('ceph.conf');
1076 my $monaddrhash = {};
1078 my $systemd_managed = PVE
::Ceph
::Tools
::systemd_managed
();
1080 foreach my $section (keys %$cfg) {
1081 next if $section eq 'global';
1082 my $d = $cfg->{$section};
1083 if ($section =~ m/^mon\./) {
1085 if ($d->{'mon addr'}) {
1086 $monaddrhash->{$d->{'mon addr'}} = $section;
1091 my $monid = $param->{id
} // $param->{node
};
1093 my $monsection = "mon.$monid";
1094 my $pubnet = $cfg->{global
}->{'public network'};
1095 my $ip = $find_mon_ip->($pubnet, $param->{node
}, $param->{'mon-address'});
1097 my $monaddr = Net
::IP
::ip_is_ipv6
($ip) ?
"[$ip]:6789" : "$ip:6789";
1098 my $monname = $param->{node
};
1100 die "monitor '$monsection' already exists\n" if $cfg->{$monsection};
1101 die "monitor address '$monaddr' already in use by '$monaddrhash->{$monaddr}'\n"
1102 if $monaddrhash->{$monaddr};
1107 my $pve_ckeyring_path = PVE
::Ceph
::Tools
::get_config
('pve_ckeyring_path');
1109 if (! -f
$pve_ckeyring_path) {
1110 run_command
("ceph-authtool $pve_ckeyring_path --create-keyring " .
1111 "--gen-key -n client.admin");
1114 my $pve_mon_key_path = PVE
::Ceph
::Tools
::get_config
('pve_mon_key_path');
1115 if (! -f
$pve_mon_key_path) {
1116 run_command
("cp $pve_ckeyring_path $pve_mon_key_path.tmp");
1117 run_command
("ceph-authtool $pve_mon_key_path.tmp -n client.admin --set-uid=0 " .
1118 "--cap mds 'allow' " .
1119 "--cap osd 'allow *' " .
1120 "--cap mgr 'allow *' " .
1121 "--cap mon 'allow *'");
1122 run_command
("cp $pve_mon_key_path.tmp /etc/ceph/ceph.client.admin.keyring") if $systemd_managed;
1123 run_command
("chown ceph:ceph /etc/ceph/ceph.client.admin.keyring") if $systemd_managed;
1124 run_command
("ceph-authtool $pve_mon_key_path.tmp --gen-key -n mon. --cap mon 'allow *'");
1125 run_command
("mv $pve_mon_key_path.tmp $pve_mon_key_path");
1128 my $ccname = PVE
::Ceph
::Tools
::get_config
('ccname');
1130 my $mondir = "/var/lib/ceph/mon/$ccname-$monid";
1131 -d
$mondir && die "monitor filesystem '$mondir' already exist\n";
1133 my $monmap = "/tmp/monmap";
1138 run_command
("chown ceph:ceph $mondir") if $systemd_managed;
1140 if ($moncount > 0) {
1141 my $rados = PVE
::RADOS-
>new(timeout
=> PVE
::Ceph
::Tools
::get_config
('long_rados_timeout'));
1142 my $mapdata = $rados->mon_command({ prefix
=> 'mon getmap', format
=> 'plain' });
1143 file_set_contents
($monmap, $mapdata);
1145 run_command
("monmaptool --create --clobber --add $monid $monaddr --print $monmap");
1148 run_command
("ceph-mon --mkfs -i $monid --monmap $monmap --keyring $pve_mon_key_path");
1149 run_command
("chown ceph:ceph -R $mondir") if $systemd_managed;
1154 File
::Path
::remove_tree
($mondir);
1158 $cfg->{$monsection} = {
1160 'mon addr' => $monaddr,
1163 cfs_write_file
('ceph.conf', $cfg);
1165 my $create_keys_pid = fork();
1166 if (!defined($create_keys_pid)) {
1167 die "Could not spawn ceph-create-keys to create bootstrap keys\n";
1168 } elsif ($create_keys_pid == 0) {
1169 exit PVE
::Tools
::run_command
(['ceph-create-keys', '-i', $monid]);
1171 PVE
::Ceph
::Tools
::ceph_service_cmd
('start', $monsection);
1173 if ($systemd_managed) {
1174 #to ensure we have the correct startup order.
1175 eval { PVE
::Tools
::run_command
(['/bin/systemctl', 'enable', "ceph-mon\@${monid}.service"]); };
1176 warn "Enable ceph-mon\@${monid}.service manually"if $@;
1178 waitpid($create_keys_pid, 0);
1182 if (!$param->{'exclude-manager'}) {
1183 my $rados = PVE
::RADOS-
>new(timeout
=> PVE
::Ceph
::Tools
::get_config
('long_rados_timeout'));
1184 $create_mgr->($rados, $monid);
1188 return $rpcenv->fork_worker('cephcreatemon', $monsection, $authuser, $worker);
1191 __PACKAGE__-
>register_method ({
1192 name
=> 'destroymon',
1193 path
=> 'mon/{monid}',
1195 description
=> "Destroy Ceph Monitor and Manager.",
1199 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1202 additionalProperties
=> 0,
1204 node
=> get_standard_option
('pve-node'),
1206 description
=> 'Monitor ID',
1208 pattern
=> '[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?',
1210 'exclude-manager' => {
1214 description
=> "When set, removes only the monitor, not the manager"
1218 returns
=> { type
=> 'string' },
1222 my $rpcenv = PVE
::RPCEnvironment
::get
();
1224 my $authuser = $rpcenv->get_user();
1226 PVE
::Ceph
::Tools
::check_ceph_inited
();
1228 my $cfg = cfs_read_file
('ceph.conf');
1230 my $monid = $param->{monid
};
1231 my $monsection = "mon.$monid";
1233 my $rados = PVE
::RADOS-
>new();
1234 my $monstat = $rados->mon_command({ prefix
=> 'mon_status' });
1235 my $monlist = $monstat->{monmap
}->{mons
};
1237 die "no such monitor id '$monid'\n"
1238 if !defined($cfg->{$monsection});
1240 my $ccname = PVE
::Ceph
::Tools
::get_config
('ccname');
1242 my $mondir = "/var/lib/ceph/mon/$ccname-$monid";
1243 -d
$mondir || die "monitor filesystem '$mondir' does not exist on this node\n";
1245 die "can't remove last monitor\n" if scalar(@$monlist) <= 1;
1250 # reopen with longer timeout
1251 $rados = PVE
::RADOS-
>new(timeout
=> PVE
::Ceph
::Tools
::get_config
('long_rados_timeout'));
1253 $rados->mon_command({ prefix
=> "mon remove", name
=> $monid, format
=> 'plain' });
1255 eval { PVE
::Ceph
::Tools
::ceph_service_cmd
('stop', $monsection); };
1258 delete $cfg->{$monsection};
1259 cfs_write_file
('ceph.conf', $cfg);
1260 File
::Path
::remove_tree
($mondir);
1263 if (!$param->{'exclude-manager'}) {
1264 eval { $destroy_mgr->($monid); };
1269 return $rpcenv->fork_worker('cephdestroymon', $monsection, $authuser, $worker);
1272 __PACKAGE__-
>register_method ({
1273 name
=> 'createmgr',
1276 description
=> "Create Ceph Manager",
1280 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1283 additionalProperties
=> 0,
1285 node
=> get_standard_option
('pve-node'),
1289 pattern
=> '[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?',
1290 description
=> "The ID for the manager, when omitted the same as the nodename",
1294 returns
=> { type
=> 'string' },
1298 PVE
::Ceph
::Tools
::check_ceph_installed
('ceph_mgr');
1300 PVE
::Ceph
::Tools
::check_ceph_inited
();
1302 my $rpcenv = PVE
::RPCEnvironment
::get
();
1304 my $authuser = $rpcenv->get_user();
1306 my $mgrid = $param->{id
} // $param->{node
};
1311 my $rados = PVE
::RADOS-
>new(timeout
=> PVE
::Ceph
::Tools
::get_config
('long_rados_timeout'));
1313 $create_mgr->($rados, $mgrid);
1316 return $rpcenv->fork_worker('cephcreatemgr', "mgr.$mgrid", $authuser, $worker);
1319 __PACKAGE__-
>register_method ({
1320 name
=> 'destroymgr',
1323 description
=> "Destroy Ceph Manager.",
1327 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1330 additionalProperties
=> 0,
1332 node
=> get_standard_option
('pve-node'),
1334 description
=> 'The ID of the manager',
1336 pattern
=> '[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?',
1340 returns
=> { type
=> 'string' },
1344 my $rpcenv = PVE
::RPCEnvironment
::get
();
1346 my $authuser = $rpcenv->get_user();
1348 PVE
::Ceph
::Tools
::check_ceph_inited
();
1350 my $mgrid = $param->{id
};
1355 $destroy_mgr->($mgrid);
1358 return $rpcenv->fork_worker('cephdestroymgr', "mgr.$mgrid", $authuser, $worker);
1361 __PACKAGE__-
>register_method ({
1365 description
=> "Stop ceph services.",
1369 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1372 additionalProperties
=> 0,
1374 node
=> get_standard_option
('pve-node'),
1376 description
=> 'Ceph service name.',
1379 default => 'ceph.target',
1380 pattern
=> '(mon|mds|osd|mgr)\.[A-Za-z0-9\-]{1,32}',
1384 returns
=> { type
=> 'string' },
1388 my $rpcenv = PVE
::RPCEnvironment
::get
();
1390 my $authuser = $rpcenv->get_user();
1392 PVE
::Ceph
::Tools
::check_ceph_inited
();
1394 my $cfg = cfs_read_file
('ceph.conf');
1395 scalar(keys %$cfg) || die "no configuration\n";
1401 if ($param->{service
}) {
1402 push @$cmd, $param->{service
};
1405 PVE
::Ceph
::Tools
::ceph_service_cmd
(@$cmd);
1408 return $rpcenv->fork_worker('srvstop', $param->{service
} || 'ceph',
1409 $authuser, $worker);
1412 __PACKAGE__-
>register_method ({
1416 description
=> "Start ceph services.",
1420 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1423 additionalProperties
=> 0,
1425 node
=> get_standard_option
('pve-node'),
1427 description
=> 'Ceph service name.',
1430 default => 'ceph.target',
1431 pattern
=> '(mon|mds|osd|mgr)\.[A-Za-z0-9\-]{1,32}',
1435 returns
=> { type
=> 'string' },
1439 my $rpcenv = PVE
::RPCEnvironment
::get
();
1441 my $authuser = $rpcenv->get_user();
1443 PVE
::Ceph
::Tools
::check_ceph_inited
();
1445 my $cfg = cfs_read_file
('ceph.conf');
1446 scalar(keys %$cfg) || die "no configuration\n";
1451 my $cmd = ['start'];
1452 if ($param->{service
}) {
1453 push @$cmd, $param->{service
};
1456 PVE
::Ceph
::Tools
::ceph_service_cmd
(@$cmd);
1459 return $rpcenv->fork_worker('srvstart', $param->{service
} || 'ceph',
1460 $authuser, $worker);
1463 __PACKAGE__-
>register_method ({
1467 description
=> "Restart ceph services.",
1471 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1474 additionalProperties
=> 0,
1476 node
=> get_standard_option
('pve-node'),
1478 description
=> 'Ceph service name.',
1481 default => 'ceph.target',
1482 pattern
=> '(mon|mds|osd|mgr)\.[A-Za-z0-9\-]{1,32}',
1486 returns
=> { type
=> 'string' },
1490 my $rpcenv = PVE
::RPCEnvironment
::get
();
1492 my $authuser = $rpcenv->get_user();
1494 PVE
::Ceph
::Tools
::check_ceph_inited
();
1496 my $cfg = cfs_read_file
('ceph.conf');
1497 scalar(keys %$cfg) || die "no configuration\n";
1502 my $cmd = ['restart'];
1503 if ($param->{service
}) {
1504 push @$cmd, $param->{service
};
1507 PVE
::Ceph
::Tools
::ceph_service_cmd
(@$cmd);
1510 return $rpcenv->fork_worker('srvrestart', $param->{service
} || 'ceph',
1511 $authuser, $worker);
1514 __PACKAGE__-
>register_method ({
1518 description
=> "Get ceph status.",
1522 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
1525 additionalProperties
=> 0,
1527 node
=> get_standard_option
('pve-node'),
1530 returns
=> { type
=> 'object' },
1534 PVE
::Ceph
::Tools
::check_ceph_enabled
();
1536 my $rados = PVE
::RADOS-
>new();
1537 my $status = $rados->mon_command({ prefix
=> 'status' });
1538 $status->{health
} = $rados->mon_command({ prefix
=> 'health', detail
=> 'detail' });
1542 __PACKAGE__-
>register_method ({
1546 description
=> "List all pools.",
1550 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
1553 additionalProperties
=> 0,
1555 node
=> get_standard_option
('pve-node'),
1563 pool
=> { type
=> 'integer' },
1564 pool_name
=> { type
=> 'string' },
1565 size
=> { type
=> 'integer' },
1568 links
=> [ { rel
=> 'child', href
=> "{pool_name}" } ],
1573 PVE
::Ceph
::Tools
::check_ceph_inited
();
1575 my $rados = PVE
::RADOS-
>new();
1578 my $res = $rados->mon_command({ prefix
=> 'df' });
1580 foreach my $d (@{$res->{pools
}}) {
1581 next if !$d->{stats
};
1582 next if !defined($d->{id
});
1583 $stats->{$d->{id
}} = $d->{stats
};
1586 $res = $rados->mon_command({ prefix
=> 'osd dump' });
1587 my $rulestmp = $rados->mon_command({ prefix
=> 'osd crush rule dump'});
1590 for my $rule (@$rulestmp) {
1591 $rules->{$rule->{rule_id
}} = $rule->{rule_name
};
1595 foreach my $e (@{$res->{pools
}}) {
1597 foreach my $attr (qw(pool pool_name size min_size pg_num crush_rule)) {
1598 $d->{$attr} = $e->{$attr} if defined($e->{$attr});
1601 if (defined($d->{crush_rule
}) && defined($rules->{$d->{crush_rule
}})) {
1602 $d->{crush_rule_name
} = $rules->{$d->{crush_rule
}};
1605 if (my $s = $stats->{$d->{pool
}}) {
1606 $d->{bytes_used
} = $s->{bytes_used
};
1607 $d->{percent_used
} = $s->{percent_used
};
1616 __PACKAGE__-
>register_method ({
1617 name
=> 'createpool',
1620 description
=> "Create POOL",
1624 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1627 additionalProperties
=> 0,
1629 node
=> get_standard_option
('pve-node'),
1631 description
=> "The name of the pool. It must be unique.",
1635 description
=> 'Number of replicas per object',
1643 description
=> 'Minimum number of replicas per object',
1651 description
=> "Number of placement groups.",
1659 description
=> "The rule to use for mapping object placement in the cluster.",
1664 description
=> "The application of the pool, 'rbd' by default.",
1666 enum
=> ['rbd', 'cephfs', 'rgw'],
1670 description
=> "Configure VM and CT storage using the new pool.",
1676 returns
=> { type
=> 'string' },
1680 PVE
::Cluster
::check_cfs_quorum
();
1681 PVE
::Ceph
::Tools
::check_ceph_inited
();
1683 my $pve_ckeyring_path = PVE
::Ceph
::Tools
::get_config
('pve_ckeyring_path');
1685 die "not fully configured - missing '$pve_ckeyring_path'\n"
1686 if ! -f
$pve_ckeyring_path;
1688 my $pool = $param->{name
};
1689 my $rpcenv = PVE
::RPCEnvironment
::get
();
1690 my $user = $rpcenv->get_user();
1692 if ($param->{add_storages
}) {
1693 $rpcenv->check($user, '/storage', ['Datastore.Allocate']);
1694 die "pool name contains characters which are illegal for storage naming\n"
1695 if !PVE
::JSONSchema
::parse_storage_id
($pool);
1698 my $pg_num = $param->{pg_num
} || 128;
1699 my $size = $param->{size
} || 3;
1700 my $min_size = $param->{min_size
} || 2;
1701 my $application = $param->{application
} // 'rbd';
1705 PVE
::Ceph
::Tools
::create_pool
($pool, $param);
1707 if ($param->{add_storages
}) {
1709 eval { $add_storage->($pool, "${pool}"); };
1711 warn "failed to add storage: $@";
1714 die "adding storage for pool '$pool' failed, check log and add manually!\n"
1719 return $rpcenv->fork_worker('cephcreatepool', $pool, $user, $worker);
1722 __PACKAGE__-
>register_method ({
1723 name
=> 'get_flags',
1726 description
=> "get all set ceph flags",
1730 check
=> ['perm', '/', [ 'Sys.Audit' ]],
1733 additionalProperties
=> 0,
1735 node
=> get_standard_option
('pve-node'),
1738 returns
=> { type
=> 'string' },
1742 PVE
::Ceph
::Tools
::check_ceph_inited
();
1744 my $pve_ckeyring_path = PVE
::Ceph
::Tools
::get_config
('pve_ckeyring_path');
1746 die "not fully configured - missing '$pve_ckeyring_path'\n"
1747 if ! -f
$pve_ckeyring_path;
1749 my $rados = PVE
::RADOS-
>new();
1751 my $stat = $rados->mon_command({ prefix
=> 'osd dump' });
1753 return $stat->{flags
} // '';
1756 __PACKAGE__-
>register_method ({
1758 path
=> 'flags/{flag}',
1760 description
=> "Set a ceph flag",
1764 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1767 additionalProperties
=> 0,
1769 node
=> get_standard_option
('pve-node'),
1771 description
=> 'The ceph flag to set/unset',
1773 enum
=> [ 'full', 'pause', 'noup', 'nodown', 'noout', 'noin', 'nobackfill', 'norebalance', 'norecover', 'noscrub', 'nodeep-scrub', 'notieragent'],
1777 returns
=> { type
=> 'null' },
1781 PVE
::Ceph
::Tools
::check_ceph_inited
();
1783 my $pve_ckeyring_path = PVE
::Ceph
::Tools
::get_config
('pve_ckeyring_path');
1785 die "not fully configured - missing '$pve_ckeyring_path'\n"
1786 if ! -f
$pve_ckeyring_path;
1788 my $set = $param->{set
} // !$param->{unset
};
1789 my $rados = PVE
::RADOS-
>new();
1791 $rados->mon_command({
1792 prefix
=> "osd set",
1793 key
=> $param->{flag
},
1799 __PACKAGE__-
>register_method ({
1800 name
=> 'unset_flag',
1801 path
=> 'flags/{flag}',
1803 description
=> "Unset a ceph flag",
1807 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1810 additionalProperties
=> 0,
1812 node
=> get_standard_option
('pve-node'),
1814 description
=> 'The ceph flag to set/unset',
1816 enum
=> [ 'full', 'pause', 'noup', 'nodown', 'noout', 'noin', 'nobackfill', 'norebalance', 'norecover', 'noscrub', 'nodeep-scrub', 'notieragent'],
1820 returns
=> { type
=> 'null' },
1824 PVE
::Ceph
::Tools
::check_ceph_inited
();
1826 my $pve_ckeyring_path = PVE
::Ceph
::Tools
::get_config
('pve_ckeyring_path');
1828 die "not fully configured - missing '$pve_ckeyring_path'\n"
1829 if ! -f
$pve_ckeyring_path;
1831 my $set = $param->{set
} // !$param->{unset
};
1832 my $rados = PVE
::RADOS-
>new();
1834 $rados->mon_command({
1835 prefix
=> "osd unset",
1836 key
=> $param->{flag
},
1842 __PACKAGE__-
>register_method ({
1843 name
=> 'destroypool',
1844 path
=> 'pools/{name}',
1846 description
=> "Destroy pool",
1850 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1853 additionalProperties
=> 0,
1855 node
=> get_standard_option
('pve-node'),
1857 description
=> "The name of the pool. It must be unique.",
1861 description
=> "If true, destroys pool even if in use",
1866 remove_storages
=> {
1867 description
=> "Remove all pveceph-managed storages configured for this pool",
1874 returns
=> { type
=> 'string' },
1878 PVE
::Ceph
::Tools
::check_ceph_inited
();
1880 my $rpcenv = PVE
::RPCEnvironment
::get
();
1881 my $user = $rpcenv->get_user();
1882 $rpcenv->check($user, '/storage', ['Datastore.Allocate'])
1883 if $param->{remove_storages
};
1885 my $pool = $param->{name
};
1888 my $storages = $get_storages->($pool);
1890 # if not forced, destroy ceph pool only when no
1891 # vm disks are on it anymore
1892 if (!$param->{force
}) {
1893 my $storagecfg = PVE
::Storage
::config
();
1894 foreach my $storeid (keys %$storages) {
1895 my $storage = $storages->{$storeid};
1897 # check if any vm disks are on the pool
1898 print "checking storage '$storeid' for RBD images..\n";
1899 my $res = PVE
::Storage
::vdisk_list
($storagecfg, $storeid);
1900 die "ceph pool '$pool' still in use by storage '$storeid'\n"
1901 if @{$res->{$storeid}} != 0;
1905 PVE
::Ceph
::Tools
::destroy_pool
($pool);
1907 if ($param->{remove_storages
}) {
1909 foreach my $storeid (keys %$storages) {
1910 # skip external clusters, not managed by pveceph
1911 next if $storages->{$storeid}->{monhost
};
1912 eval { PVE
::API2
::Storage
::Config-
>delete({storage
=> $storeid}) };
1914 warn "failed to remove storage '$storeid': $@\n";
1918 die "failed to remove (some) storages - check log and remove manually!\n"
1922 return $rpcenv->fork_worker('cephdestroypool', $pool, $user, $worker);
1926 __PACKAGE__-
>register_method ({
1930 description
=> "Get OSD crush map",
1934 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
1937 additionalProperties
=> 0,
1939 node
=> get_standard_option
('pve-node'),
1942 returns
=> { type
=> 'string' },
1946 PVE
::Ceph
::Tools
::check_ceph_inited
();
1948 # this produces JSON (difficult to read for the user)
1949 # my $txt = &$run_ceph_cmd_text(['osd', 'crush', 'dump'], quiet => 1);
1953 my $mapfile = "/var/tmp/ceph-crush.map.$$";
1954 my $mapdata = "/var/tmp/ceph-crush.txt.$$";
1956 my $rados = PVE
::RADOS-
>new();
1959 my $bindata = $rados->mon_command({ prefix
=> 'osd getcrushmap', format
=> 'plain' });
1960 file_set_contents
($mapfile, $bindata);
1961 run_command
(['crushtool', '-d', $mapfile, '-o', $mapdata]);
1962 $txt = file_get_contents
($mapdata);
1974 __PACKAGE__-
>register_method({
1978 description
=> "Read ceph log",
1981 check
=> ['perm', '/nodes/{node}', [ 'Sys.Syslog' ]],
1985 additionalProperties
=> 0,
1987 node
=> get_standard_option
('pve-node'),
2006 description
=> "Line number",
2010 description
=> "Line text",
2019 PVE
::Ceph
::Tools
::check_ceph_inited
();
2021 my $rpcenv = PVE
::RPCEnvironment
::get
();
2022 my $user = $rpcenv->get_user();
2023 my $node = $param->{node
};
2025 my $logfile = "/var/log/ceph/ceph.log";
2026 my ($count, $lines) = PVE
::Tools
::dump_logfile
($logfile, $param->{start
}, $param->{limit
});
2028 $rpcenv->set_result_attrib('total', $count);
2033 __PACKAGE__-
>register_method ({
2037 description
=> "List ceph rules.",
2041 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
2044 additionalProperties
=> 0,
2046 node
=> get_standard_option
('pve-node'),
2055 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
2060 PVE
::Ceph
::Tools
::check_ceph_inited
();
2062 my $rados = PVE
::RADOS-
>new();
2064 my $rules = $rados->mon_command({ prefix
=> 'osd crush rule ls' });
2068 foreach my $rule (@$rules) {
2069 push @$res, { name
=> $rule };