From 9504801b02cd16e8abced2bc678e1f3b94826adc Mon Sep 17 00:00:00 2001 From: John Mulligan Date: Sat, 18 Nov 2023 13:58:33 -0500 Subject: [PATCH] cephadm: add method for parsing a sub identity from a service name This will become handy later when we need DaemonSubIdentity objects and are not on the deploy path. Signed-off-by: John Mulligan --- src/cephadm/cephadmlib/daemon_identity.py | 46 ++++++++++++++++- src/cephadm/tests/test_util_funcs.py | 62 +++++++++++++++++++++++ 2 files changed, 107 insertions(+), 1 deletion(-) diff --git a/src/cephadm/cephadmlib/daemon_identity.py b/src/cephadm/cephadmlib/daemon_identity.py index cbefa15041a0c..760401a3e5402 100644 --- a/src/cephadm/cephadmlib/daemon_identity.py +++ b/src/cephadm/cephadmlib/daemon_identity.py @@ -5,7 +5,7 @@ import os import pathlib import re -from typing import Union, Optional +from typing import Union, Optional, Tuple from .context import CephadmContext @@ -177,6 +177,50 @@ class DaemonSubIdentity(DaemonIdentity): subcomponent, ) + @classmethod + def from_service_name( + cls, service_name: str + ) -> Tuple['DaemonSubIdentity', str]: + """Return a DaemonSubIdentity and category value by parsing the + contents of a systemd service name for a sidecar container. + """ + # ceph services always have the template@instance form + tpart, ipart = service_name.split('@', 1) + # drop the .service if it exists + if ipart.endswith('.service'): + ipart = ipart[:-8] + # verify the service name starts with 'ceph' -- our framework + framework, tpart = tpart.split('-', 1) + if framework != 'ceph': + raise ValueError(f'Invalid framework value: {service_name}') + # we're parsing only services for subcomponents. it must take the + # form -. Where categories are sidecar or init. + fsid, category = tpart.rsplit('-', 1) + try: + Categories(category) + except ValueError: + raise ValueError(f'Invalid service category: {service_name}') + # if it is a sidecar it will have a subcomponent name following a colon + svcparts = ipart.split(':') + if len(svcparts) == 1: + subc = '' + elif len(svcparts) == 2: + subc = svcparts[1] + else: + raise ValueError(f'Unexpected instance value: {ipart}') + # only services based on sidecars currently have named subcomponents + # init subcomponents are all "hidden" within a single init service + if subc and not category == Categories.SIDECAR: + raise ValueError( + f'Unexpected subcomponent {subc!r} for category {category}' + ) + elif not subc: + # because we return a DaemonSubIdentity we need some value for + # the subcomponent on init services. Just repeat the category + subc = str(category) + daemon_type, daemon_id = svcparts[0].split('.', 1) + return cls(fsid, daemon_type, daemon_id, subc), category + @classmethod def must(cls, value: Optional[DaemonIdentity]) -> 'DaemonSubIdentity': """Helper to assert value is of the correct type. Mostly to make mypy diff --git a/src/cephadm/tests/test_util_funcs.py b/src/cephadm/tests/test_util_funcs.py index ffcf3909c4ee1..c52852db679ff 100644 --- a/src/cephadm/tests/test_util_funcs.py +++ b/src/cephadm/tests/test_util_funcs.py @@ -810,3 +810,65 @@ def test_apply_deploy_config_to_ctx(cc, monkeypatch): ctx = FakeContext() _cephadm.apply_deploy_config_to_ctx(cc.cfg_data, ctx) cc.check(ctx) + + +def test_daemon_sub_identity_from_sidecar_service(): + from cephadmlib.daemon_identity import DaemonSubIdentity + + dsi = DaemonSubIdentity( + '244c9842-866b-11ee-80ad-3497f6318048', 'iscsi', 'rab.oof', 'tcmu' + ) + service_name = dsi.sidecar_service_name + assert ( + service_name + == 'ceph-244c9842-866b-11ee-80ad-3497f6318048-sidecar@iscsi.rab.oof:tcmu.service' + ) + d2, category = DaemonSubIdentity.from_service_name(service_name) + assert category == 'sidecar' + assert d2.fsid == '244c9842-866b-11ee-80ad-3497f6318048' + assert d2.daemon_type == 'iscsi' + assert d2.daemon_id == 'rab.oof' + assert d2.subcomponent == 'tcmu' + + +def test_daemon_sub_identity_from_init_service(): + from cephadmlib.daemon_identity import DaemonIdentity, DaemonSubIdentity + + di = DaemonIdentity( + '244c9842-866b-11ee-80ad-3497f6318048', 'putrats', 'wow', + ) + service_name = di.init_service_name + assert ( + service_name + == 'ceph-244c9842-866b-11ee-80ad-3497f6318048-init@putrats.wow.service' + ) + d2, category = DaemonSubIdentity.from_service_name(service_name) + assert category == 'init' + assert d2.fsid == '244c9842-866b-11ee-80ad-3497f6318048' + assert d2.daemon_type == 'putrats' + assert d2.daemon_id == 'wow' + assert d2.subcomponent == 'init' + + +def test_daemon_sub_identity_from_service_invalid(): + from cephadmlib.daemon_identity import DaemonSubIdentity + + service_name = 'ceph-244c9842-866b-11ee-80ad-3497f6318048-morbo@iscsi.rab.oof.tcmu.service' + with pytest.raises(ValueError): + DaemonSubIdentity.from_service_name(service_name) + + service_name = 'ceph-244c9842-866b-11ee-80ad-3497f6318048@iscsi.rab.oof.service' + with pytest.raises(ValueError): + DaemonSubIdentity.from_service_name(service_name) + + service_name = 'ceph-244c9842-866b-11ee-80ad-3497f6318048-sidecar@foo.bar.baz:acolon:toomany.service' + with pytest.raises(ValueError): + DaemonSubIdentity.from_service_name(service_name) + + service_name = 'ceph-244c9842-866b-11ee-80ad-3497f6318048-init@foo.bar.baz:woops.service' + with pytest.raises(ValueError): + DaemonSubIdentity.from_service_name(service_name) + + service_name = 'random-task@elsewise.service' + with pytest.raises(ValueError): + DaemonSubIdentity.from_service_name(service_name) -- 2.39.5