]>
git.proxmox.com Git - pve-manager.git/blob - PVE/API2/Ceph/OSD.pm
1 package PVE
::API2
::Ceph
::OSD
;
12 use PVE
::Ceph
::Services
;
14 use PVE
::Cluster
qw(cfs_read_file cfs_write_file);
16 use PVE
::Storage
::LVMPlugin
;
17 use PVE
::Exception
qw(raise_param_exc);
18 use PVE
::JSONSchema
qw(get_standard_option);
22 use PVE
::RPCEnvironment
;
23 use PVE
::Tools
qw(run_command file_set_contents);
27 use base
qw(PVE::RESTHandler);
29 my $nodename = PVE
::INotify
::nodename
();
31 my $get_osd_status = sub {
32 my ($rados, $osdid) = @_;
34 my $stat = $rados->mon_command({ prefix
=> 'osd dump' });
36 my $osdlist = $stat->{osds
} || [];
38 my $flags = $stat->{flags
} || undef;
41 foreach my $d (@$osdlist) {
42 $osdstat->{$d->{osd
}} = $d if defined($d->{osd
});
44 if (defined($osdid)) {
45 die "no such OSD '$osdid'\n" if !$osdstat->{$osdid};
46 return $osdstat->{$osdid};
49 return wantarray ?
($osdstat, $flags) : $osdstat;
52 my $get_osd_usage = sub {
55 my $osdlist = $rados->mon_command({ prefix
=> 'pg dump', dumpcontents
=> [ 'osds' ]});
56 if (!($osdlist && ref($osdlist))) {
57 warn "got unknown result format for 'pg dump osds' command\n";
61 if (ref($osdlist) eq "HASH") { # since nautilus
62 $osdlist = $osdlist->{osd_stats
};
66 for my $d (@$osdlist) {
67 $osdstat->{$d->{osd
}} = $d if defined($d->{osd
});
73 __PACKAGE__-
>register_method ({
77 description
=> "Get Ceph osd list/tree.",
81 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
84 additionalProperties
=> 0,
86 node
=> get_standard_option
('pve-node'),
89 # fixme: return a list instead of extjs tree format ?
95 flags
=> { type
=> "string" },
98 description
=> "Tree with OSDs in the CRUSH map structure.",
106 PVE
::Ceph
::Tools
::check_ceph_inited
();
108 my $rados = PVE
::RADOS-
>new();
109 my $res = $rados->mon_command({ prefix
=> 'osd df', output_method
=> 'tree', });
111 die "no tree nodes found\n" if !($res && $res->{nodes
});
113 my ($osdhash, $flags) = $get_osd_status->($rados);
115 my $osd_usage = $get_osd_usage->($rados);
117 my $osdmetadata_res = $rados->mon_command({ prefix
=> 'osd metadata' });
118 my $osdmetadata = { map { $_->{id
} => $_ } @$osdmetadata_res };
120 my $hostversions = PVE
::Ceph
::Services
::get_ceph_versions
();
124 foreach my $e (@{$res->{nodes
}}) {
125 my ($id, $name) = $e->@{qw(id name)};
135 foreach my $opt (qw(status crush_weight reweight device_class pgs)) {
136 $new->{$opt} = $e->{$opt} if defined($e->{$opt});
139 if (my $stat = $osdhash->{$id}) {
140 $new->{in} = $stat->{in} if defined($stat->{in});
143 if (my $stat = $osd_usage->{$id}) {
144 $new->{total_space
} = ($stat->{kb
} || 1) * 1024;
145 $new->{bytes_used
} = ($stat->{kb_used
} || 0) * 1024;
146 $new->{percent_used
} = ($new->{bytes_used
}*100)/$new->{total_space
};
147 if (my $d = $stat->{perf_stat
}) {
148 $new->{commit_latency_ms
} = $d->{commit_latency_ms
};
149 $new->{apply_latency_ms
} = $d->{apply_latency_ms
};
153 my $osdmd = $osdmetadata->{$id};
154 if ($e->{type
} eq 'osd' && $osdmd) {
155 if ($osdmd->{bluefs
}) {
156 $new->{osdtype
} = 'bluestore';
157 $new->{blfsdev
} = $osdmd->{bluestore_bdev_dev_node
};
158 $new->{dbdev
} = $osdmd->{bluefs_db_dev_node
};
159 $new->{waldev
} = $osdmd->{bluefs_wal_dev_node
};
161 $new->{osdtype
} = 'filestore';
163 for my $field (qw(ceph_version ceph_version_short)) {
164 $new->{$field} = $osdmd->{$field} if $osdmd->{$field};
168 $newnodes->{$id} = $new;
171 foreach my $e (@{$res->{nodes
}}) {
172 my ($id, $name) = $e->@{qw(id name)};
173 my $new = $newnodes->{$id};
175 if ($e->{children
} && scalar(@{$e->{children
}})) {
176 $new->{children
} = [];
178 foreach my $cid (@{$e->{children
}}) {
179 $nodes->{$cid}->{parent
} = $id;
180 if ($nodes->{$cid}->{type
} eq 'osd' && $e->{type
} eq 'host') {
181 $newnodes->{$cid}->{host
} = $name;
183 push @{$new->{children
}}, $newnodes->{$cid};
186 $new->{leaf
} = ($id >= 0) ?
1 : 0;
189 if ($name && $e->{type
} eq 'host') {
190 $new->{version
} = $hostversions->{$name}->{version
}->{str
};
195 foreach my $e (@{$res->{nodes
}}) {
197 if (!$nodes->{$id}->{parent
}) {
198 push @$realroots, $newnodes->{$id};
202 die "no root node\n" if scalar(@$realroots) < 1;
207 children
=> $realroots
211 $data->{flags
} = $flags if $flags; # we want this for the noout flag
216 __PACKAGE__-
>register_method ({
220 description
=> "Create OSD",
224 additionalProperties
=> 0,
226 node
=> get_standard_option
('pve-node'),
228 description
=> "Block device name.",
232 description
=> "Block device name for block.db.",
237 description
=> "Size in GiB for block.db.",
238 verbose_description
=> "If a block.db is requested but the size is not given, ".
239 "will be automatically selected by: bluestore_block_db_size from the ".
240 "ceph database (osd or global section) or config (osd or global section)".
241 "in that order. If this is not available, it will be sized 10% of the size ".
242 "of the OSD device. Fails if the available size is not enough.",
245 default => 'bluestore_block_db_size or 10% of OSD size',
246 requires
=> 'db_dev',
250 description
=> "Block device name for block.wal.",
255 description
=> "Size in GiB for block.wal.",
256 verbose_description
=> "If a block.wal is requested but the size is not given, ".
257 "will be automatically selected by: bluestore_block_wal_size from the ".
258 "ceph database (osd or global section) or config (osd or global section)".
259 "in that order. If this is not available, it will be sized 1% of the size ".
260 "of the OSD device. Fails if the available size is not enough.",
263 default => 'bluestore_block_wal_size or 1% of OSD size',
264 requires
=> 'wal_dev',
271 description
=> "Enables encryption of the OSD."
273 'crush-device-class' => {
276 description
=> "Set the device class of the OSD in crush."
280 returns
=> { type
=> 'string' },
284 my $rpcenv = PVE
::RPCEnvironment
::get
();
286 my $authuser = $rpcenv->get_user();
288 # test basic requirements
289 PVE
::Ceph
::Tools
::check_ceph_inited
();
290 PVE
::Ceph
::Tools
::setup_pve_symlinks
();
291 PVE
::Ceph
::Tools
::check_ceph_installed
('ceph_osd');
292 PVE
::Ceph
::Tools
::check_ceph_installed
('ceph_volume');
294 # extract parameter info and fail if a device is set more than once
297 my $ceph_conf = cfs_read_file
('ceph.conf');
299 my $osd_network = $ceph_conf->{global
}->{cluster_network
};
300 $osd_network //= $ceph_conf->{global
}->{public_network
}; # fallback
302 if ($osd_network) { # check only if something is configured
303 my $cluster_net_ips = PVE
::Network
::get_local_ip_from_cidr
($osd_network);
304 if (scalar(@$cluster_net_ips) < 1) {
305 my $osd_net_obj = PVE
::Network
::IP_from_cidr
($osd_network);
306 my $osd_base_cidr = $osd_net_obj->{ip
} . "/" . $osd_net_obj->{prefixlen
};
308 die "No address from ceph cluster network (${osd_base_cidr}) found on node '$nodename'. ".
309 "Check your network config.\n";
313 for my $type ( qw(dev db_dev wal_dev) ) {
314 next if !$param->{$type};
316 my $type_dev = PVE
::Diskmanage
::verify_blockdev_path
($param->{$type});
317 (my $type_devname = $type_dev) =~ s
|/dev/||;
319 raise_param_exc
({ $type => "cannot chose '$type_dev' for more than one type." })
320 if grep { $_->{name
} eq $type_devname } values %$devs;
324 name
=> $type_devname,
327 if (my $size = $param->{"${type}_size"}) {
328 $devs->{$type}->{size
} = PVE
::Tools
::convert_size
($size, 'gb' => 'b') ;
332 my $test_disk_requirements = sub {
335 my $dev = $devs->{dev
}->{dev
};
336 my $devname = $devs->{dev
}->{name
};
337 die "unable to get device info for '$dev'\n" if !$disklist->{$devname};
338 die "device '$dev' is already in use\n" if $disklist->{$devname}->{used
};
340 for my $type ( qw(db_dev wal_dev) ) {
341 my $d = $devs->{$type};
343 my $name = $d->{name
};
344 my $info = $disklist->{$name};
345 die "unable to get device info for '$d->{dev}' for type $type\n" if !$disklist->{$name};
346 if (my $usage = $info->{used
}) {
347 if ($usage eq 'partitions') {
348 die "device '$d->{dev}' is not GPT partitioned\n" if !$info->{gpt
};
349 } elsif ($usage ne 'LVM') {
350 die "device '$d->{dev}' is already in use and has no LVM on it\n";
357 # test disk requirements early
358 my $devlist = [ map { $_->{name
} } values %$devs ];
359 my $disklist = PVE
::Diskmanage
::get_disks
($devlist, 1, 1);
360 $test_disk_requirements->($disklist);
362 # get necessary ceph infos
363 my $rados = PVE
::RADOS-
>new();
364 my $monstat = $rados->mon_command({ prefix
=> 'quorum_status' });
366 die "unable to get fsid\n" if !$monstat->{monmap
} || !$monstat->{monmap
}->{fsid
};
367 my $fsid = $monstat->{monmap
}->{fsid
};
368 $fsid = $1 if $fsid =~ m/^([0-9a-f\-]+)$/;
370 my $ceph_bootstrap_osd_keyring = PVE
::Ceph
::Tools
::get_config
('ceph_bootstrap_osd_keyring');
372 if (! -f
$ceph_bootstrap_osd_keyring && $ceph_conf->{global
}->{auth_client_required
} eq 'cephx') {
373 my $bindata = $rados->mon_command({
374 prefix
=> 'auth get-or-create',
375 entity
=> 'client.bootstrap-osd',
377 'mon' => 'allow profile bootstrap-osd'
381 file_set_contents
($ceph_bootstrap_osd_keyring, $bindata);
385 my @udev_trigger_devs = ();
387 my $create_part_or_lv = sub {
388 my ($dev, $size, $type) = @_;
390 $size =~ m/^(\d+)$/ or die "invalid size '$size'\n";
393 die "'$dev->{devpath}' is smaller than requested size '$size' bytes\n"
394 if $dev->{size
} < $size;
396 # sgdisk and lvcreate can only sizes divisible by 512b
397 # so we round down to the nearest kb
398 $size = PVE
::Tools
::convert_size
($size, 'b' => 'kb', 1);
403 my $vg = "ceph-" . UUID
::uuid
();
404 my $lv = $type . "-" . UUID
::uuid
();
406 PVE
::Storage
::LVMPlugin
::lvm_create_volume_group
($dev->{devpath
}, $vg);
407 PVE
::Storage
::LVMPlugin
::lvcreate
($vg, $lv, "${size}k");
409 if (PVE
::Diskmanage
::is_partition
($dev->{devpath
})) {
410 eval { PVE
::Diskmanage
::change_parttype
($dev->{devpath
}, '8E00'); };
414 push @udev_trigger_devs, $dev->{devpath
};
418 } elsif ($dev->{used
} eq 'LVM') {
419 # check pv/vg and create lv
421 my $vgs = PVE
::Storage
::LVMPlugin
::lvm_vgs
(1);
423 for my $vgname ( sort keys %$vgs ) {
424 next if $vgname !~ /^ceph-/;
426 for my $pv ( @{$vgs->{$vgname}->{pvs
}} ) {
427 next if $pv->{name
} ne $dev->{devpath
};
434 die "no ceph vg found on '$dev->{devpath}'\n" if !$vg;
435 die "vg '$vg' has not enough free space\n" if $vgs->{$vg}->{free
} < $size;
437 my $lv = $type . "-" . UUID
::uuid
();
439 PVE
::Storage
::LVMPlugin
::lvcreate
($vg, $lv, "${size}k");
443 } elsif ($dev->{used
} eq 'partitions' && $dev->{gpt
}) {
444 # create new partition at the end
446 'osd-db' => '30CD0809-C2B2-499C-8879-2D6B78529876',
447 'osd-wal' => '5CE17FCE-4087-4169-B7FF-056CC58473F9',
450 my $part = PVE
::Diskmanage
::append_partition
($dev->{devpath
}, $size * 1024);
452 if (my $parttype = $parttypes->{$type}) {
453 eval { PVE
::Diskmanage
::change_parttype
($part, $parttype); };
457 push @udev_trigger_devs, $part;
461 die "cannot use '$dev->{devpath}' for '$type'\n";
467 PVE
::Diskmanage
::locked_disk_action
(sub {
468 # update disklist and re-test requirements
469 $disklist = PVE
::Diskmanage
::get_disks
($devlist, 1, 1);
470 $test_disk_requirements->($disklist);
472 my $dev_class = $param->{'crush-device-class'};
473 my $cmd = ['ceph-volume', 'lvm', 'create', '--cluster-fsid', $fsid ];
474 push @$cmd, '--crush-device-class', $dev_class if $dev_class;
476 my $devname = $devs->{dev
}->{name
};
477 my $devpath = $disklist->{$devname}->{devpath
};
478 print "create OSD on $devpath (bluestore)\n";
480 push @udev_trigger_devs, $devpath;
482 my $osd_size = $disklist->{$devname}->{size
};
484 db
=> int($osd_size / 10), # 10% of OSD
485 wal
=> int($osd_size / 100), # 1% of OSD
489 foreach my $type ( qw(db wal) ) {
490 my $fallback_size = $size_map->{$type};
491 my $d = $devs->{"${type}_dev"};
494 # size was not set via api, getting from config/fallback
495 if (!defined($d->{size
})) {
496 $sizes = PVE
::Ceph
::Tools
::get_db_wal_sizes
() if !$sizes;
497 $d->{size
} = $sizes->{$type} // $fallback_size;
499 print "creating block.$type on '$d->{dev}'\n";
500 my $name = $d->{name
};
501 my $part_or_lv = $create_part_or_lv->($disklist->{$name}, $d->{size
}, "osd-$type");
503 print "using '$part_or_lv' for block.$type\n";
504 push @$cmd, "--block.$type", $part_or_lv;
507 push @$cmd, '--data', $devpath;
508 push @$cmd, '--dmcrypt' if $param->{encrypted
};
510 PVE
::Diskmanage
::wipe_blockdev
($devpath);
512 if (PVE
::Diskmanage
::is_partition
($devpath)) {
513 eval { PVE
::Diskmanage
::change_parttype
($devpath, '8E00'); };
519 # FIXME: Remove once we depend on systemd >= v249.
520 # Work around udev bug https://github.com/systemd/systemd/issues/18525 to ensure the
521 # udev database is updated.
522 eval { run_command
(['udevadm', 'trigger', @udev_trigger_devs]); };
527 return $rpcenv->fork_worker('cephcreateosd', $devs->{dev
}->{name
}, $authuser, $worker);
530 my $OSD_DEV_RETURN_PROPS = {
533 enum
=> ['block', 'db', 'wal'],
534 description
=> 'Kind of OSD device',
538 description
=> 'Device node',
542 description
=> 'Physical disks used',
546 description
=> 'Size in bytes',
550 description
=> 'Discard support of the physical device',
554 description
=> 'Type of device. For example, hdd or ssd',
558 __PACKAGE__-
>register_method ({
562 permissions
=> { user
=> 'all' },
563 description
=> "OSD index.",
565 additionalProperties
=> 0,
567 node
=> get_standard_option
('pve-node'),
569 description
=> 'OSD ID',
580 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
586 { name
=> 'metadata' },
587 { name
=> 'lv-info' },
593 __PACKAGE__-
>register_method ({
594 name
=> 'osddetails',
595 path
=> '{osdid}/metadata',
597 description
=> "Get OSD details",
601 check
=> ['perm', '/', [ 'Sys.Audit' ], any
=> 1],
604 additionalProperties
=> 0,
606 node
=> get_standard_option
('pve-node'),
608 description
=> 'OSD ID',
618 description
=> 'General information about the OSD',
622 description
=> 'Name of the host containing the OSD.',
626 description
=> 'ID of the OSD.',
630 description
=> 'Memory usage of the OSD service.',
634 description
=> "Path to the OSD's data directory.",
638 description
=> 'The type of object store used.',
642 description
=> 'OSD process ID.',
646 description
=> 'Ceph version of the OSD service.',
650 description
=> 'Address and port used to talk to clients and monitors.',
654 description
=> 'Address and port used to talk to other OSDs.',
658 description
=> 'Heartbeat address and port for clients and monitors.',
662 description
=> 'Heartbeat address and port for other OSDs.',
668 description
=> 'Array containing data about devices',
671 properties
=> $OSD_DEV_RETURN_PROPS,
679 PVE
::Ceph
::Tools
::check_ceph_inited
();
681 my $osdid = $param->{osdid
};
682 my $rados = PVE
::RADOS-
>new();
683 my $metadata = $rados->mon_command({ prefix
=> 'osd metadata', id
=> int($osdid) });
685 die "OSD '${osdid}' does not exists on host '${nodename}'\n"
686 if $nodename ne $metadata->{hostname
};
692 if ($line =~ m/^MainPID=([0-9]*)$/) {
700 "ceph-osd\@${osdid}.service",
704 run_command
($cmd, errmsg
=> 'fetching OSD PID and memory usage failed', outfunc
=> $parser);
706 $pid = defined($pid) ?
int($pid) : undef;
709 if ($pid && $pid > 0) {
710 open (my $SMAPS, '<', "/proc/$pid/smaps_rollup")
711 or die "failed to read PSS memory-stat from process - $!\n";
713 while (my $line = <$SMAPS>) {
714 if ($line =~ m/^Pss:\s+([0-9]+) kB$/) {
725 hostname
=> $metadata->{hostname
},
726 id
=> $metadata->{id
},
727 mem_usage
=> $memory,
728 osd_data
=> $metadata->{osd_data
},
729 osd_objectstore
=> $metadata->{osd_objectstore
},
731 version
=> "$metadata->{ceph_version_short} ($metadata->{ceph_release})",
732 front_addr
=> $metadata->{front_addr
},
733 back_addr
=> $metadata->{back_addr
},
734 hb_front_addr
=> $metadata->{hb_front_addr
},
735 hb_back_addr
=> $metadata->{hb_back_addr
},
739 $data->{devices
} = [];
742 my ($dev, $prefix, $device) = @_;
746 dev_node
=> $metadata->{"${prefix}_${dev}_dev_node"},
747 physical_device
=> $metadata->{"${prefix}_${dev}_devices"},
748 size
=> int($metadata->{"${prefix}_${dev}_size"}),
749 support_discard
=> int($metadata->{"${prefix}_${dev}_support_discard"}),
750 type
=> $metadata->{"${prefix}_${dev}_type"},
756 $get_data->("bdev", "bluestore", "block");
757 $get_data->("db", "bluefs", "db") if $metadata->{bluefs_dedicated_db
};
758 $get_data->("wal", "bluefs", "wal") if $metadata->{bluefs_dedicated_wal
};
763 __PACKAGE__-
>register_method ({
765 path
=> '{osdid}/lv-info',
767 description
=> "Get OSD volume details",
771 check
=> ['perm', '/', [ 'Sys.Audit' ], any
=> 1],
774 additionalProperties
=> 0,
776 node
=> get_standard_option
('pve-node'),
778 description
=> 'OSD ID',
782 description
=> 'OSD device type',
784 enum
=> ['block', 'db', 'wal'],
795 description
=> "Creation time as reported by `lvs`.",
799 description
=> 'Name of the logical volume (LV).',
803 description
=> 'Path to the logical volume (LV).',
807 description
=> 'Size of the logical volume (LV).',
811 description
=> 'UUID of the logical volume (LV).',
815 description
=> 'Name of the volume group (VG).',
822 PVE
::Ceph
::Tools
::check_ceph_inited
();
824 my $osdid = $param->{osdid
};
825 my $type = $param->{type
} // 'block';
828 my $parser = sub { $raw .= shift };
829 my $cmd = ['/usr/sbin/ceph-volume', 'lvm', 'list', $osdid, '--format', 'json'];
830 run_command
($cmd, errmsg
=> 'listing Ceph LVM volumes failed', outfunc
=> $parser);
833 if ($raw =~ m/^(\{.*\})$/s) { #untaint
834 $result = JSON
::decode_json
($1);
836 die "got unexpected data from ceph-volume: '${raw}'\n";
838 if (!$result->{$osdid}) {
839 die "OSD '${osdid}' not found in 'ceph-volume lvm list' on node '${nodename}'.\n"
840 ."Maybe it was created before LVM became the default?\n";
843 my $lv_data = { map { $_->{type
} => $_ } @{$result->{$osdid}} };
844 my $volume = $lv_data->{$type} || die "volume type '${type}' not found for OSD ${osdid}\n";
847 $cmd = ['/sbin/lvs', $volume->{lv_path
}, '--reportformat', 'json', '-o', 'lv_time'];
848 run_command
($cmd, errmsg
=> 'listing logical volumes failed', outfunc
=> $parser);
850 if ($raw =~ m/(\{.*\})$/s) { #untaint, lvs has whitespace at beginning
851 $result = JSON
::decode_json
($1);
853 die "got unexpected data from lvs: '${raw}'\n";
856 my $data = { map { $_ => $volume->{$_} } qw(lv_name lv_path lv_uuid vg_name) };
857 $data->{lv_size
} = int($volume->{lv_size
});
859 $data->{creation_time
} = @{$result->{report
}}[0]->{lv
}[0]->{lv_time
};
864 # Check if $osdid belongs to $nodename
865 # $tree ... rados osd tree (passing the tree makes it easy to test)
866 sub osd_belongs_to_node
{
867 my ($tree, $nodename, $osdid) = @_;
868 return 0 if !($tree && $tree->{nodes
});
871 for my $el (grep { defined($_->{type
}) && $_->{type
} eq 'host' } @{$tree->{nodes
}}) {
872 my $name = $el->{name
};
873 die "internal error: duplicate host name found '$name'\n" if $node_map->{$name};
874 $node_map->{$name} = $el;
877 my $osds = $node_map->{$nodename}->{children
};
880 return grep($_ == $osdid, @$osds);
883 __PACKAGE__-
>register_method ({
884 name
=> 'destroyosd',
887 description
=> "Destroy OSD",
891 additionalProperties
=> 0,
893 node
=> get_standard_option
('pve-node'),
895 description
=> 'OSD ID',
899 description
=> "If set, we remove partition table entries.",
906 returns
=> { type
=> 'string' },
910 my $rpcenv = PVE
::RPCEnvironment
::get
();
912 my $authuser = $rpcenv->get_user();
914 PVE
::Ceph
::Tools
::check_ceph_inited
();
916 my $osdid = $param->{osdid
};
917 my $cleanup = $param->{cleanup
};
919 my $rados = PVE
::RADOS-
>new();
921 my $osd_belongs_to_node = osd_belongs_to_node
(
922 $rados->mon_command({ prefix
=> 'osd tree' }),
926 die "OSD osd.$osdid does not belong to node $param->{node}!"
927 if !$osd_belongs_to_node;
929 # dies if osdid is unknown
930 my $osdstat = $get_osd_status->($rados, $osdid);
932 die "osd is in use (in == 1)\n" if $osdstat->{in};
933 #&$run_ceph_cmd(['osd', 'out', $osdid]);
935 die "osd is still running (up == 1)\n" if $osdstat->{up
};
937 my $osdsection = "osd.$osdid";
942 # reopen with longer timeout
943 $rados = PVE
::RADOS-
>new(timeout
=> PVE
::Ceph
::Tools
::get_config
('long_rados_timeout'));
945 print "destroy OSD $osdsection\n";
948 PVE
::Ceph
::Services
::ceph_service_cmd
('stop', $osdsection);
949 PVE
::Ceph
::Services
::ceph_service_cmd
('disable', $osdsection);
953 print "Remove $osdsection from the CRUSH map\n";
954 $rados->mon_command({ prefix
=> "osd crush remove", name
=> $osdsection, format
=> 'plain' });
956 print "Remove the $osdsection authentication key.\n";
957 $rados->mon_command({ prefix
=> "auth del", entity
=> $osdsection, format
=> 'plain' });
959 print "Remove OSD $osdsection\n";
960 $rados->mon_command({ prefix
=> "osd rm", ids
=> [ $osdsection ], format
=> 'plain' });
962 # try to unmount from standard mount point
963 my $mountpoint = "/var/lib/ceph/osd/ceph-$osdid";
966 my $udev_trigger_devs = {};
968 my $remove_partition = sub {
971 return if !$part || (! -b
$part );
972 my $partnum = PVE
::Diskmanage
::get_partnum
($part);
973 my $devpath = PVE
::Diskmanage
::get_blockdev
($part);
975 $udev_trigger_devs->{$devpath} = 1;
977 PVE
::Diskmanage
::wipe_blockdev
($part);
978 print "remove partition $part (disk '${devpath}', partnum $partnum)\n";
979 eval { run_command
(['/sbin/sgdisk', '-d', $partnum, "${devpath}"]); };
983 my $osd_list = PVE
::Ceph
::Tools
::ceph_volume_list
();
985 if ($osd_list->{$osdid}) { # ceph-volume managed
987 eval { PVE
::Ceph
::Tools
::ceph_volume_zap
($osdid, $cleanup) };
991 # try to remove pvs, but do not fail if it does not work
992 for my $osd_part (@{$osd_list->{$osdid}}) {
993 for my $dev (@{$osd_part->{devices
}}) {
994 ($dev) = ($dev =~ m
|^(/dev/[-_
.a-zA-Z0-9\
/]+)$|); #untaint
996 eval { run_command
(['/sbin/pvremove', $dev], errfunc
=> sub {}) };
999 $udev_trigger_devs->{$dev} = 1;
1004 my $partitions_to_remove = [];
1006 if (my $mp = PVE
::ProcFSTools
::parse_proc_mounts
()) {
1007 foreach my $line (@$mp) {
1008 my ($dev, $path, $fstype) = @$line;
1009 next if !($dev && $path && $fstype);
1010 next if $dev !~ m
|^/dev/|;
1012 if ($path eq $mountpoint) {
1013 abs_path
($dev) =~ m
|^(/.+)| or die "invalid dev: $dev\n";
1014 push @$partitions_to_remove, $1;
1020 foreach my $path (qw(journal block block.db block.wal)) {
1021 abs_path
("$mountpoint/$path") =~ m
|^(/.+)| or die "invalid path: $path\n";
1022 push @$partitions_to_remove, $1;
1026 print "Unmount OSD $osdsection from $mountpoint\n";
1027 eval { run_command
(['/bin/umount', $mountpoint]); };
1030 } elsif ($cleanup) {
1031 #be aware of the ceph udev rules which can remount.
1032 foreach my $part (@$partitions_to_remove) {
1033 $remove_partition->($part);
1038 # FIXME: Remove once we depend on systemd >= v249.
1039 # Work around udev bug https://github.com/systemd/systemd/issues/18525 to ensure the
1040 # udev database is updated.
1042 eval { run_command
(['udevadm', 'trigger', keys $udev_trigger_devs->%*]); };
1047 return $rpcenv->fork_worker('cephdestroyosd', $osdsection, $authuser, $worker);
1050 __PACKAGE__-
>register_method ({
1052 path
=> '{osdid}/in',
1054 description
=> "ceph osd in",
1058 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1061 additionalProperties
=> 0,
1063 node
=> get_standard_option
('pve-node'),
1065 description
=> 'OSD ID',
1070 returns
=> { type
=> "null" },
1074 PVE
::Ceph
::Tools
::check_ceph_inited
();
1076 my $osdid = $param->{osdid
};
1078 my $rados = PVE
::RADOS-
>new();
1080 $get_osd_status->($rados, $osdid); # osd exists?
1082 my $osdsection = "osd.$osdid";
1084 $rados->mon_command({ prefix
=> "osd in", ids
=> [ $osdsection ], format
=> 'plain' });
1089 __PACKAGE__-
>register_method ({
1091 path
=> '{osdid}/out',
1093 description
=> "ceph osd out",
1097 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1100 additionalProperties
=> 0,
1102 node
=> get_standard_option
('pve-node'),
1104 description
=> 'OSD ID',
1109 returns
=> { type
=> "null" },
1113 PVE
::Ceph
::Tools
::check_ceph_inited
();
1115 my $osdid = $param->{osdid
};
1117 my $rados = PVE
::RADOS-
>new();
1119 $get_osd_status->($rados, $osdid); # osd exists?
1121 my $osdsection = "osd.$osdid";
1123 $rados->mon_command({ prefix
=> "osd out", ids
=> [ $osdsection ], format
=> 'plain' });
1128 __PACKAGE__-
>register_method ({
1130 path
=> '{osdid}/scrub',
1132 description
=> "Instruct the OSD to scrub.",
1136 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1139 additionalProperties
=> 0,
1141 node
=> get_standard_option
('pve-node'),
1143 description
=> 'OSD ID',
1147 description
=> 'If set, instructs a deep scrub instead of a normal one.',
1154 returns
=> { type
=> "null" },
1158 PVE
::Ceph
::Tools
::check_ceph_inited
();
1160 my $osdid = $param->{osdid
};
1161 my $deep = $param->{deep
} // 0;
1163 my $rados = PVE
::RADOS-
>new();
1165 $get_osd_status->($rados, $osdid); # osd exists?
1167 my $prefix = $deep ?
'osd deep-scrub' : 'osd scrub';
1168 $rados->mon_command({ prefix
=> $prefix, who
=> $osdid });