From 02a13f2de0783ff3c680a6042388a4692250edfb Mon Sep 17 00:00:00 2001 From: Andrew Schoen Date: Mon, 16 Jul 2018 16:06:03 -0500 Subject: [PATCH] ceph-volume: an osd ID must exist and be destroyed before reuse Fixes: https://tracker.ceph.com/issues/24044 Signed-off-by: Andrew Schoen --- .../ceph_volume/tests/util/test_prepare.py | 27 ++++++++++++------- src/ceph-volume/ceph_volume/util/prepare.py | 18 ++++++++----- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/src/ceph-volume/ceph_volume/tests/util/test_prepare.py b/src/ceph-volume/ceph_volume/tests/util/test_prepare.py index 23dce21c5641f..8148a7f2d18f4 100644 --- a/src/ceph-volume/ceph_volume/tests/util/test_prepare.py +++ b/src/ceph-volume/ceph_volume/tests/util/test_prepare.py @@ -7,24 +7,24 @@ from ceph_volume import conf from ceph_volume.tests.conftest import Factory -class TestCheckID(object): +class TestOSDIDAvailable(object): def test_false_if_id_is_none(self): - assert not prepare.check_id(None) + assert not prepare.osd_id_available(None) def test_returncode_is_not_zero(self, monkeypatch): monkeypatch.setattr('ceph_volume.process.call', lambda *a, **kw: ('', '', 1)) with pytest.raises(RuntimeError): - prepare.check_id(1) + prepare.osd_id_available(1) - def test_id_does_exist(self, monkeypatch): + def test_id_does_exist_but_not_available(self, monkeypatch): stdout = dict(nodes=[ - dict(id=0), + dict(id=0, status="up"), ]) stdout = ['', json.dumps(stdout)] monkeypatch.setattr('ceph_volume.process.call', lambda *a, **kw: (stdout, '', 0)) - result = prepare.check_id(0) - assert result + result = prepare.osd_id_available(0) + assert not result def test_id_does_not_exist(self, monkeypatch): stdout = dict(nodes=[ @@ -32,7 +32,7 @@ class TestCheckID(object): ]) stdout = ['', json.dumps(stdout)] monkeypatch.setattr('ceph_volume.process.call', lambda *a, **kw: (stdout, '', 0)) - result = prepare.check_id(1) + result = prepare.osd_id_available(1) assert not result def test_invalid_osd_id(self, monkeypatch): @@ -41,9 +41,18 @@ class TestCheckID(object): ]) stdout = ['', json.dumps(stdout)] monkeypatch.setattr('ceph_volume.process.call', lambda *a, **kw: (stdout, '', 0)) - result = prepare.check_id("foo") + result = prepare.osd_id_available("foo") assert not result + def test_returns_true_when_id_is_destroyed(self, monkeypatch): + stdout = dict(nodes=[ + dict(id=0, status="destroyed"), + ]) + stdout = ['', json.dumps(stdout)] + monkeypatch.setattr('ceph_volume.process.call', lambda *a, **kw: (stdout, '', 0)) + result = prepare.osd_id_available(0) + assert result + class TestFormatDevice(object): diff --git a/src/ceph-volume/ceph_volume/util/prepare.py b/src/ceph-volume/ceph_volume/util/prepare.py index 59d60fc0cbeeb..32d5fe20d53bd 100644 --- a/src/ceph-volume/ceph_volume/util/prepare.py +++ b/src/ceph-volume/ceph_volume/util/prepare.py @@ -64,8 +64,11 @@ def create_id(fsid, json_secrets, osd_id=None): '-i', '-', 'osd', 'new', fsid ] - if osd_id is not None and not osd_id_exists(osd_id): - cmd.append(osd_id) + if osd_id is not None: + if osd_id_available(osd_id): + cmd.append(osd_id) + else: + raise RuntimeError("The osd ID {} is already in use or does not exist.".format(osd_id)) stdout, stderr, returncode = process.call( cmd, stdin=json_secrets, @@ -76,10 +79,10 @@ def create_id(fsid, json_secrets, osd_id=None): return ' '.join(stdout).strip() -def osd_id_exists(osd_id): +def osd_id_available(osd_id): """ - Checks to see if an osd ID exists or not. Returns True - if it does exist, False if it doesn't. + Checks to see if an osd ID exists and if it's available for + reuse. Returns True if it is, False if it isn't. :param osd_id: The osd ID to check """ @@ -103,7 +106,10 @@ def osd_id_exists(osd_id): output = json.loads(''.join(stdout).strip()) osds = output['nodes'] - return any([str(osd['id']) == str(osd_id) for osd in osds]) + osd = [osd for osd in osds if str(osd['id']) == str(osd_id)] + if osd and osd[0].get('status') == "destroyed": + return True + return False def mount_tmpfs(path): -- 2.39.5