Skip to content

Commit

Permalink
orch: disk replacement enhancement
Browse files Browse the repository at this point in the history
This introduces a new `ceph orch device replace` command in order to
improve the user experience when it comes to replacing the underlying
device of an OSD.

Fixes: https://tracker.ceph.com/issues/68456

Signed-off-by: Guillaume Abrioux <[email protected]>
  • Loading branch information
guits committed Oct 16, 2024
1 parent b5e7008 commit 212c874
Show file tree
Hide file tree
Showing 23 changed files with 1,151 additions and 70 deletions.
69 changes: 69 additions & 0 deletions doc/cephadm/operations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -734,3 +734,72 @@ Purge ceph daemons from all hosts in the cluster

# For each host:
cephadm rm-cluster --force --zap-osds --fsid <fsid>


Replacing a device
==================

The ``ceph orch device replace`` command automates the process of replacing the underlying device of an OSD.
Previously, this process required manual intervention at various stages.
With this new command, all necessary operations are performed automatically, streamlining the replacement process
and improving the overall user experience.

.. note:: This only supports LVM-based deployed OSD(s)

.. prompt:: bash #

ceph orch device replace <host> <device-path>

In the case the device being replaced is shared by multiple OSDs (eg: DB/WAL device shared by multiple OSDs), the orchestrator will warn you.

.. prompt:: bash #

[ceph: root@ceph /]# ceph orch device replace osd-1 /dev/vdd

Error EINVAL: /dev/vdd is a shared device.
Replacing /dev/vdd implies destroying OSD(s): ['0', '1'].
Please, *be very careful*, this can be a very dangerous operation.
If you know what you are doing, pass --yes-i-really-mean-it

If you know what you are doing, you can go ahead and pass ``--yes-i-really-mean-it``.

.. prompt:: bash #

[ceph: root@ceph /]# ceph orch device replace osd-1 /dev/vdd --yes-i-really-mean-it
Scheduled to destroy osds: ['6', '7', '8'] and mark /dev/vdd as being replaced.

``cephadm`` will make ``ceph-volume`` zap and destroy all related devices and mark the corresponding OSD as ``destroyed`` so the
different OSD(s) ID(s) will be preserved:

.. prompt:: bash #

[ceph: root@ceph-1 /]# ceph osd tree
ID CLASS WEIGHT TYPE NAME STATUS REWEIGHT PRI-AFF
-1 0.97659 root default
-3 0.97659 host devel-1
0 hdd 0.29300 osd.0 destroyed 1.00000 1.00000
1 hdd 0.29300 osd.1 destroyed 1.00000 1.00000
2 hdd 0.19530 osd.2 up 1.00000 1.00000
3 hdd 0.19530 osd.3 up 1.00000 1.00000

The device being replaced is finally seen as ``being replaced`` preventing ``cephadm`` from redeploying the OSDs too fast:

.. prompt:: bash #

[ceph: root@ceph-1 /]# ceph orch device ls
HOST PATH TYPE DEVICE ID SIZE AVAILABLE REFRESHED REJECT REASONS
osd-1 /dev/vdb hdd 200G Yes 13s ago
osd-1 /dev/vdc hdd 200G Yes 13s ago
osd-1 /dev/vdd hdd 200G Yes 13s ago Is being replaced
osd-1 /dev/vde hdd 200G No 13s ago Has a FileSystem, Insufficient space (<10 extents) on vgs, LVM detected
osd-1 /dev/vdf hdd 200G No 13s ago Has a FileSystem, Insufficient space (<10 extents) on vgs, LVM detected

If for any reason you need to clear the 'device replace header' on a device, then you can use ``ceph orch device replace <host> <device> --clear``:

.. prompt:: bash #

[ceph: root@devel-1 /]# ceph orch device replace devel-1 /dev/vdk --clear
Replacement header cleared on /dev/vdk
[ceph: root@devel-1 /]#

After that, ``cephadm`` will redeploy the OSD service spec within a few minutes (unless the service is set to ``unmanaged``).
1 change: 1 addition & 0 deletions src/ceph-volume/ceph_volume/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
sys_info = namedtuple('sys_info', ['devices'])
sys_info.devices = dict()
logger = logging.getLogger(__name__)
BEING_REPLACED_HEADER: str = 'CEPH_DEVICE_BEING_REPLACED'


class AllowLoopDevices:
Expand Down
17 changes: 11 additions & 6 deletions src/ceph-volume/ceph_volume/api/lvm.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
from math import floor
from ceph_volume import process, util, conf
from ceph_volume.exceptions import SizeAllocationError
from typing import Any, Dict


logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -807,13 +809,16 @@ def get_all_devices_vgs(name_prefix=''):
'--units=b', '--nosuffix']


class Volume(object):
class Volume:
"""
Represents a Logical Volume from LVM, with some top-level attributes like
``lv_name`` and parsed tags as a dictionary of key/value pairs.
"""

def __init__(self, **kw):
def __init__(self, **kw: str) -> None:
self.lv_path: str = ''
self.lv_name: str = ''
self.lv_uuid: str = ''
for k, v in kw.items():
setattr(self, k, v)
self.lv_api = kw
Expand All @@ -824,13 +829,13 @@ def __init__(self, **kw):
self.encrypted = self.tags.get('ceph.encrypted', '0') == '1'
self.used_by_ceph = 'ceph.osd_id' in self.tags

def __str__(self):
def __str__(self) -> str:
return '<%s>' % self.lv_api['lv_path']

def __repr__(self):
def __repr__(self) -> str:
return self.__str__()

def as_dict(self):
def as_dict(self) -> Dict[str, Any]:
obj = {}
obj.update(self.lv_api)
obj['tags'] = self.tags
Expand All @@ -839,7 +844,7 @@ def as_dict(self):
obj['path'] = self.lv_path
return obj

def report(self):
def report(self) -> Dict[str, Any]:
if not self.used_by_ceph:
return {
'name': self.lv_name,
Expand Down
Loading

0 comments on commit 212c874

Please sign in to comment.