import pathlib
import re
-from typing import Union, Optional
+from typing import Union, Optional, Tuple
from .context import CephadmContext
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 <FSID>-<CATEGORY>. 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
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)