|
85 | 85 | concurrent_tasks, |
86 | 86 | ) |
87 | 87 | from cephadmlib.container_engines import ( |
| 88 | + ImageInfo, |
88 | 89 | Podman, |
89 | 90 | check_container_engine, |
90 | 91 | find_container_engine, |
|
202 | 203 | MemUsageStatusUpdater, |
203 | 204 | VersionStatusUpdater, |
204 | 205 | ) |
205 | | -from cephadmlib.container_lookup import get_container_info |
206 | 206 |
|
207 | 207 |
|
208 | 208 | FuncT = TypeVar('FuncT', bound=Callable) |
@@ -466,42 +466,82 @@ def update_default_image(ctx: CephadmContext) -> None: |
466 | 466 | ctx.image = _get_default_image(ctx) |
467 | 467 |
|
468 | 468 |
|
469 | | -def infer_local_ceph_image(ctx: CephadmContext, container_path: str) -> Optional[str]: |
470 | | - """ |
471 | | - Infer the local ceph image based on the following priority criteria: |
472 | | - 1- the image specified by --image arg (if provided). |
473 | | - 2- the same image as the daemon container specified by --name arg (if provided). |
474 | | - 3- image used by any ceph container running on the host. In this case we use daemon types. |
475 | | - 4- if no container is found then we use the most ceph recent image on the host. |
476 | | -
|
477 | | - Note: any selected container must have the same fsid inferred previously. |
| 469 | +def infer_local_ceph_image( |
| 470 | + ctx: CephadmContext, container_path: str = '' |
| 471 | +) -> Optional[str]: |
| 472 | + """Infer the best ceph image to use based on the following criteria: |
| 473 | + Out of all images labeled as ceph that are non-dangling, prefer |
| 474 | + 1. the same image as the daemon container specified by -name arg (if provided). |
| 475 | + 2. the image used by any ceph container running on the host |
| 476 | + 3. the most ceph recent image on the host |
478 | 477 |
|
479 | | - :return: The most recent local ceph image (already pulled) |
| 478 | + :return: An image name or none |
480 | 479 | """ |
| 480 | + from operator import itemgetter |
| 481 | + |
| 482 | + # enumerate ceph images on the system |
481 | 483 | images = parsed_container_image_list( |
482 | 484 | ctx, |
483 | 485 | filters=['dangling=false', 'label=ceph=True'], |
484 | 486 | container_path=container_path, |
485 | 487 | ) |
| 488 | + if not images: |
| 489 | + logger.warning('No non-dangling ceph images found') |
| 490 | + return None # no images at all cached on host |
| 491 | + |
| 492 | + # find running ceph daemons |
| 493 | + _daemons = ceph_daemons() |
| 494 | + daemon_name = getattr(ctx, 'name', '') |
| 495 | + _cinfo_key = '_container_info' |
| 496 | + _updater = CoreStatusUpdater(keep_container_info=_cinfo_key) |
| 497 | + matching_daemons = [ |
| 498 | + itemgetter(_cinfo_key, 'name')(_updater.expand(ctx, entry)) |
| 499 | + for entry in daemons_matching( |
| 500 | + ctx, fsid=ctx.fsid, daemon_type_predicate=lambda t: t in _daemons |
| 501 | + ) |
| 502 | + ] |
| 503 | + # collect the running ceph daemon image ids |
| 504 | + images_in_use_by_daemon = set( |
| 505 | + d.image_id for d, n in matching_daemons if n == daemon_name |
| 506 | + ) |
| 507 | + images_in_use = set(d.image_id for d, _ in matching_daemons) |
| 508 | + |
| 509 | + # prioritize images |
| 510 | + def _keyfunc(image: ImageInfo) -> Tuple[bool, bool, str]: |
| 511 | + return ( |
| 512 | + bool( |
| 513 | + image.digest |
| 514 | + and any( |
| 515 | + v.startswith(image.image_id) |
| 516 | + for v in images_in_use_by_daemon |
| 517 | + ) |
| 518 | + ), |
| 519 | + bool( |
| 520 | + image.digest |
| 521 | + and any(v.startswith(image.image_id) for v in images_in_use) |
| 522 | + ), |
| 523 | + image.created, |
| 524 | + ) |
486 | 525 |
|
487 | | - container_info = None |
488 | | - daemon_name = ctx.name if ('name' in ctx and ctx.name and '.' in ctx.name) else None |
489 | | - daemons_ls = [daemon_name] if daemon_name is not None else ceph_daemons() # daemon types: 'mon', 'mgr', etc |
490 | | - for daemon in daemons_ls: |
491 | | - container_info = get_container_info(ctx, daemon, daemon_name is not None) |
492 | | - if container_info is not None: |
493 | | - logger.debug(f"Using container info for daemon '{daemon}'") |
494 | | - break |
495 | | - |
496 | | - for image in images: |
497 | | - if container_info is not None and image.image_id not in container_info.image_id: |
498 | | - continue |
499 | | - if image.digest: |
500 | | - logger.info(f"Using ceph image with id '{image.image_id}' and tag '{image.tag}' created on {image.created}\n{image.name}") |
501 | | - return image.name |
502 | | - if container_info is not None: |
503 | | - logger.warning(f"Not using image '{container_info.image_id}' as it's not in list of non-dangling images with ceph=True label") |
504 | | - return None |
| 526 | + images.sort(key=_keyfunc, reverse=True) |
| 527 | + best_image = images[0] |
| 528 | + name_match, ceph_match, _ = _keyfunc(best_image) |
| 529 | + reason = 'not in the list of non-dangling images with ceph=True label' |
| 530 | + if images_in_use_by_daemon and not name_match: |
| 531 | + expected = list(images_in_use_by_daemon)[0] |
| 532 | + logger.warning( |
| 533 | + 'Not using image %r of named daemon: %s', |
| 534 | + expected, |
| 535 | + reason, |
| 536 | + ) |
| 537 | + if images_in_use and not ceph_match: |
| 538 | + expected = list(images_in_use)[0] |
| 539 | + logger.warning( |
| 540 | + 'Not using image %r of ceph daemon: %s', |
| 541 | + expected, |
| 542 | + reason, |
| 543 | + ) |
| 544 | + return best_image.name |
505 | 545 |
|
506 | 546 |
|
507 | 547 | def get_log_dir(fsid, log_dir): |
|
0 commit comments