class_docstr: Class representing an IPP cluster
  
  i.e. one controller and one or more groups of engines
  
  Can start/stop/monitor/poll cluster resources
  
  All async methods can be called synchronously with a `_sync` suffix,
  e.g. `cluster.start_cluster_sync()`
  
  .. versionchanged:: 8.0
      controller and engine launcher classes can be specified via
      `Cluster(controller='ssh', engines='mpi')`
      without the `_launcher_class` suffix.
docstr: Callback when a controller stops
docstr_tok: ["Callback","when","a","controller","stops"]
code: def _controller_stopped(self, stop_data=None):
      """Callback when a controller stops"""
      if stop_data and stop_data.get("exit_code"):
          log = self.log.warning
      else:
          log = self.log.info
      log(f"Controller stopped: {stop_data}")
      self.update_cluster_file()
  
imports: import asyncio
  import atexit
  import glob
  import inspect
  import json
  import logging
  import os
  import random
  import string
  import sys
  import time
  import traceback
  from functools import partial
  from multiprocessing import cpu_count
  from weakref import WeakSet
  import IPython
  from traitlets import Any, Bool, Dict, Float, Instance, Integer, List, Unicode, default, import_item, validate
  from traitlets.config import Application, Config, LoggingConfigurable
  from .._async import AsyncFirst
  from ..traitlets import Launcher
  from ..util import _all_profile_dirs, _default_profile_dir, _locate_profiles, _traitlet_signature, abbreviate_profile_dir
  from .None import launcher
label_desc: Use image node_id 21 for calling the Cluster obj's underlying member method code with example usage: obj._controller_stopped(stop_data) without return types