Skip to content

Commit 736833f

Browse files
phlogistonjohnmergify[bot]
authored andcommitted
sambacc: extend rados pseudo-uris to include getting mon config keys
There are cases where certain configuration items may not be stored in the Ceph cluster's rados pool but rather in the Ceph MON's configuration key store. This can be accessed via a mon command. Add support for new pseudo uri's formed like: rados:mon-config-key:foo/bar/baz Instead of fetching an item from a rados pool, this will try to fetch a key in a manner equivalent to running the following CLI command: `ceph config-key get foo/bar/baz` This idea is borrowed from the ceph iscsi support. Signed-off-by: John Mulligan <[email protected]>
1 parent 293e850 commit 736833f

File tree

2 files changed

+51
-0
lines changed

2 files changed

+51
-0
lines changed

sambacc/rados_opener.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
from __future__ import annotations
2020

21+
import io
22+
import json
2123
import typing
2224
import urllib.request
2325

@@ -39,6 +41,10 @@ class _RADOSHandler(urllib.request.BaseHandler):
3941
def rados_open(self, req: urllib.request.Request) -> typing.IO:
4042
if self._rados_api is None:
4143
raise RADOSUnsupported()
44+
if req.selector.startswith("mon-config-key:"):
45+
return _get_mon_config_key(
46+
self._rados_api, req.selector.split(":", 1)[1]
47+
)
4248
sel = req.selector.lstrip("/")
4349
if req.host:
4450
pool = req.host
@@ -171,6 +177,25 @@ def writelines(self, ls: typing.Iterable[typing.Any]) -> None:
171177
raise NotImplementedError()
172178

173179

180+
def _get_mon_config_key(rados_api: _RADOSModule, key: str) -> io.BytesIO:
181+
mcmd = json.dumps(
182+
{
183+
"prefix": "config-key get",
184+
"key": str(key),
185+
}
186+
)
187+
with rados_api.Rados(conffile=rados_api.Rados.DEFAULT_CONF_FILES) as rc:
188+
ret, out, err = rc.mon_command(mcmd, b"")
189+
if ret == 0:
190+
# We need to return a file like object. Since we are handed just
191+
# bytes from this api, use BytesIO to adapt it to something valid.
192+
return io.BytesIO(out)
193+
# ensure ceph didn't send us a negative errno
194+
ret = ret if ret > 0 else -ret
195+
msg = f"failed to get mon config key: {key!r}: {err}"
196+
raise OSError(ret, msg)
197+
198+
174199
def enable_rados_url_opener(cls: typing.Type[url_opener.URLOpener]) -> None:
175200
"""Extend the URLOpener type to support pseudo-URLs for rados
176201
object storage. If rados libraries are not found the function

tests/test_rados_opener.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,3 +156,29 @@ def test_rados_response_not_implemented():
156156
rr.write(b"zzzzz")
157157
with pytest.raises(NotImplementedError):
158158
rr.writelines([b"zzzzz"])
159+
160+
161+
def test_rados_handler_config_key():
162+
class RH(sambacc.rados_opener._RADOSHandler):
163+
_rados_api = unittest.mock.MagicMock()
164+
165+
mc = RH._rados_api.Rados.return_value.__enter__.return_value.mon_command
166+
mc.return_value = (0, b"rubber baby buggy bumpers", "")
167+
168+
rh = RH()
169+
rq = urllib.request.Request("rados:mon-config-key:aa/bb/cc")
170+
rr = rh.rados_open(rq)
171+
assert isinstance(rr, io.BytesIO)
172+
assert rr.read() == b"rubber baby buggy bumpers"
173+
assert mc.called
174+
assert "aa/bb/cc" in mc.call_args[0][0]
175+
176+
mc.reset_mock()
177+
mc.return_value = (2, b"", "no passing")
178+
rh = RH()
179+
rq = urllib.request.Request("rados:mon-config-key:xx/yy/zz")
180+
with pytest.raises(OSError) as pe:
181+
rh.rados_open(rq)
182+
assert getattr(pe.value, "errno", None) == 2
183+
assert mc.called
184+
assert "xx/yy/zz" in mc.call_args[0][0]

0 commit comments

Comments
 (0)