From: John Mulligan Date: Tue, 8 Apr 2025 23:06:45 +0000 (-0400) Subject: cephadm: support --infer-name option for lazy devs X-Git-Tag: v20.1.0~151^2~2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=d7ba3cb08eaa0b885171bbcd68d0eac0f22e2ac1;p=ceph.git cephadm: support --infer-name option for lazy devs Add an --infer-name/-i option to the cephadm enter command. This new option is a spin on --name/-n but allows the value to be partial. The first part of the value must be a service type (like `mgr`, `mds`, `nfs`, etc). That can then be followed optionally by a dot (.) and a part (or whole) of an id. For example: Enter the one and only mgr container running on this host: ``` cephadm enter -i mgr ``` Enter a (primary) smb container running on this host belonging to the virtual cluster "cluster1", without specifying random chars or rank values: ``` cephadm enter -i smb.cluster1 ``` If the partial name does not match any services on the host or it matches more than 1 service on the host it will return an error. In the case of >1 service you can then supply more characters in the partial id to narrow down the match. For example: ``` cephadm enter -i osd Inferring fsid bf7116b2-2b9d-11f0-bb35-525400220000 ERROR: too many daemons match 'osd' (osd.2, osd.5) /tmp/cephadm enter -i osd.2 Inferring fsid bf7116b2-2b9d-11f0-bb35-525400220000 Inferring daemon osd.2 [ceph: root@ceph0 /]# ``` Signed-off-by: John Mulligan (cherry picked from commit 211e677f1d42ea3de76981128b18f1c914841d27) --- diff --git a/src/cephadm/cephadm.py b/src/cephadm/cephadm.py index ac5ab21e3a4a..7a332c2ac812 100755 --- a/src/cephadm/cephadm.py +++ b/src/cephadm/cephadm.py @@ -188,9 +188,9 @@ from cephadmlib.daemons import ( ) from cephadmlib.agent import http_query from cephadmlib.listing import ( + CombinedStatusUpdater, DaemonStatusUpdater, NoOpDaemonStatusUpdater, - CombinedStatusUpdater, daemons_matching, daemons_summary, ) @@ -201,7 +201,7 @@ from cephadmlib.listing_updaters import ( MemUsageStatusUpdater, VersionStatusUpdater, ) -from cephadmlib.container_lookup import infer_local_ceph_image +from cephadmlib.container_lookup import infer_local_ceph_image, identify FuncT = TypeVar('FuncT', bound=Callable) @@ -3151,12 +3151,9 @@ def command_shell(ctx): @infer_fsid -def command_enter(ctx): - # type: (CephadmContext) -> int - if not ctx.fsid: - raise Error('must pass --fsid to specify cluster') - (daemon_type, daemon_id) = ctx.name.split('.', 1) - container_args = ['-i'] # type: List[str] +def command_enter(ctx: CephadmContext) -> int: + ident = identify(ctx) + container_args = ['-i'] if ctx.command: command = ctx.command else: @@ -3168,10 +3165,10 @@ def command_enter(ctx): ] c = CephContainer( ctx, + identity=ident, image=ctx.image, entrypoint='doesnotmatter', container_args=container_args, - cname='ceph-%s-%s.%s' % (ctx.fsid, daemon_type, daemon_id), ) command = c.exec_cmd(command) return call_timeout(ctx, command, ctx.timeout) @@ -4767,10 +4764,13 @@ def _get_parser(): parser_enter.add_argument( '--fsid', help='cluster FSID') - parser_enter.add_argument( + parser_enter_ng = parser_enter.add_mutually_exclusive_group(required=True) + parser_enter_ng.add_argument( '--name', '-n', - required=True, help='daemon name (type.id)') + parser_enter_ng.add_argument( + '--infer-name', '-i', + help='daemon name search (type[.partial_id])') parser_enter.add_argument( 'command', nargs=argparse.REMAINDER, help='command') diff --git a/src/cephadm/cephadmlib/container_lookup.py b/src/cephadm/cephadmlib/container_lookup.py index 3d44b57d8d4a..1d1d96d6ac8f 100644 --- a/src/cephadm/cephadmlib/container_lookup.py +++ b/src/cephadm/cephadmlib/container_lookup.py @@ -3,6 +3,7 @@ from operator import itemgetter from typing import Optional, Tuple +import fnmatch import logging from .container_engines import ( @@ -15,7 +16,8 @@ from .container_types import get_container_stats from .context import CephadmContext from .daemon_identity import DaemonIdentity from .daemons.ceph import ceph_daemons -from .listing import daemons_matching +from .exceptions import Error +from .listing import LegacyDaemonEntry, daemons_matching from .listing_updaters import CoreStatusUpdater @@ -174,3 +176,56 @@ def infer_local_ceph_image( reason, ) return best_image.name + + +def infer_daemon_identity( + ctx: CephadmContext, partial_name: str +) -> DaemonIdentity: + """Given a partial daemon/service name, infer the identity of the + daemon. + """ + + if not partial_name: + raise Error('a daemon type is required to infer a service name') + if '.' in partial_name: + _type, _name = partial_name.split('.', 1) + else: + _type, _name = partial_name, '' + # allow searching for a name with just the beginning without having + # to expliclity supply a trailing asterisk + _name += '*' + matches = [] + for d in daemons_matching(ctx, fsid=ctx.fsid, daemon_type=_type): + if isinstance(d, LegacyDaemonEntry): + logger.info('Ignoring legacy daemon %s', d.name) + continue # ignore legacy daemons + if fnmatch.fnmatch(d.identity.daemon_id, _name): + matches.append(d) + + if not matches: + raise Error(f'no daemons match {partial_name!r}') + if len(matches) > 1: + excess = ', '.join(d.identity.daemon_name for d in matches) + raise Error(f'too many daemons match {partial_name!r} ({excess})') + ident = matches[0].identity + logger.info('Inferring daemon %s', ident.daemon_name) + return ident + + +def identify(ctx: CephadmContext) -> DaemonIdentity: + """Given a context try and determine a specific daemon identity to use + based on the --name CLI option (exact) or --infer-name (partial match) + option. + """ + if not ctx.fsid: + raise Error('must pass --fsid to specify cluster') + name = getattr(ctx, 'name', '') + if name: + return DaemonIdentity.from_name(ctx.fsid, name) + iname = getattr(ctx, 'infer_name', '') + if iname: + return infer_daemon_identity(ctx, iname) + raise Error( + 'must specify a daemon name' + ' (use --name/-n or --infer-name/-i for example)' + )