This is an automated email from the ASF dual-hosted git repository. ssulav pushed a commit to branch HDDS-14755 in repository https://gitbox.apache.org/repos/asf/ozone-installer.git
commit 99728a295516961055b3033a5ee81e09ac8a92d7 Author: Soumitra Sulav <[email protected]> AuthorDate: Thu Mar 5 00:42:35 2026 +0530 HDDS-14755. Add actions instead of options for start, stop & clean --- ozone_installer.py | 382 ++++++++++++++++++++++++------------------- playbooks/cluster.yml | 71 ++++++-- playbooks/stop.yml | 22 --- playbooks/stop_and_clean.yml | 36 ---- 4 files changed, 276 insertions(+), 235 deletions(-) diff --git a/ozone_installer.py b/ozone_installer.py index b4c046a..c9b9796 100755 --- a/ozone_installer.py +++ b/ozone_installer.py @@ -105,10 +105,29 @@ def parse_args(argv: List[str]) -> argparse.Namespace: p.add_argument("-r", "--role-file", help="Role file (YAML) for HA mapping (optional)") p.add_argument("-j", "--jdk-version", type=int, choices=[17, 21], help="JDK major version (default: 17)") p.add_argument("-c", "--config-dir", help="Config dir (optional, templates are used by default)") - p.add_argument("-x", "--clean", action="store_true", help="(Reserved) Cleanup before install [not yet implemented]") - p.add_argument("--skip-s3g", action="store_true", help="Skip S3 Gateway Installation") - p.add_argument("--stop-only", action="store_true", help="Stop Ozone processes and exit (no cleanup)") - p.add_argument("--stop-and-clean", action="store_true", help="Stop Ozone processes, remove install and data dirs, then exit") + p.add_argument( + "actions", + nargs="+", + choices=["clean", "install", "start", "stop"], + metavar="ACTION", + help=( + "One or more actions to perform (at least one required):\n" + " install download, install, configure and start the cluster\n" + " start start services (cluster must already be installed)\n" + " stop stop all services\n" + " clean stop all services and remove all install/data dirs\n" + "\n'install' is inclusive of 'start' (no need to pass both).\n" + "'clean' is inclusive of 'stop' (services are stopped before dirs are removed).\n" + "'start' and 'stop' cannot be combined.\n" + "\nExamples:\n" + " install → install and start\n" + " start → start only\n" + " stop → stop only\n" + " clean → stop + remove all dirs\n" + " clean install → full clean reinstall\n" + ), + ) + p.add_argument("--skip-s3g", action="store_true", help="Skip S3 Gateway installation and smoke test") p.add_argument("-l", "--ssh-user", help="SSH username (default: root)") p.add_argument("-S", "--use-sudo", action="store_true", help="Run remote commands via sudo (default)") p.add_argument("-u", "--service-user", help="Service user (default: ozone)") @@ -525,6 +544,26 @@ def run_playbook(playbook: Path, inventory_path: Path, extra_vars_path: Path, as logger.info(f"Running: {' '.join(shlex.quote(c) for c in cmd)}") return subprocess.call(cmd, env=env) +def _normalize_actions(actions: List[str]) -> List[str]: + """'install' is inclusive of 'start': add 'start' automatically when only 'install' is given.""" + result = list(actions) + if "install" in result and "start" not in result: + result.append("start") + return result + + +def _validate_actions(actions: List[str], logger: logging.Logger) -> bool: + valid = {"clean", "install", "start", "stop"} + unknown = [a for a in actions if a not in valid] + if unknown: + logger.error(f"Error: unknown action(s): {', '.join(unknown)}. Valid: {', '.join(sorted(valid))}") + return False + if "start" in actions and "stop" in actions: + logger.error("Error: 'start' and 'stop' cannot be combined.") + return False + return True + + def main(argv: List[str]) -> int: args = parse_args(argv) # Resume mode: reuse last provided configs and suppress prompts when possible @@ -590,10 +629,19 @@ def main(argv: List[str]) -> int: logger.error("Error: No hosts provided (-H/--host or -F/--host-file).") return 2 - stop_only = bool(getattr(args, "stop_only", False)) - stop_and_clean = bool(getattr(args, "stop_and_clean", False)) + # --- Actions: parse, normalize and validate --- + actions = _normalize_actions(list(args.actions)) + if not _validate_actions(actions, logger): + return 2 + + do_stop = "stop" in actions or "clean" in actions + do_clean = "clean" in actions + do_install = "install" in actions + do_start = "start" in actions # 'install' already normalized to include 'start' + + logger.info(f"Actions: {', '.join(actions)}") - # Decide HA vs Non-HA with user input; default depends on master count + # --- Cluster mode (always needed) --- master_count = len(master_hosts) if use_master_worker else len(hosts) resume_cluster_mode = (last_cfg.get("cluster_mode") if last_cfg else None) if args.cluster_mode: @@ -610,7 +658,7 @@ def main(argv: List[str]) -> int: logger.error("Error: HA requires at least 3 master hosts (to map 3 OMs and 3 SCMs).") return 2 - # Auth (needed for all modes: install, stop-only, stop-and-clean) + # --- SSH auth (always needed to reach remote hosts) --- auth_method = args.auth_method or (last_cfg.get("auth_method") if last_cfg else None) \ or prompt("SSH authentication method (key or password)", default="password", yes_mode=yes) if auth_method not in ("key", "password"): @@ -628,28 +676,41 @@ def main(argv: List[str]) -> int: elif auth_method == "key": password = None - # Stop modes: gather minimal config, then fall through to common inventory + playbook run - python_interpreter = None - if stop_only: - playbook = PLAYBOOKS_DIR / "stop.yml" - extra_vars = {"cluster_mode": cluster_mode, "ssh_user": ssh_user, "controller_logs_dir": str(LOGS_DIR)} - logger.info("Running stop only on cluster...") - elif stop_and_clean: - install_base = args.install_dir or (last_cfg.get("install_base") if last_cfg else None) or prompt("Install directory to remove", default=DEFAULTS["install_base"], yes_mode=yes) - data_base_raw = args.data_dir or (last_cfg.get("data_base") if last_cfg else None) or prompt("Data directory to remove", default=DEFAULTS["data_base"], yes_mode=yes) + # --- Service identity (needed by install, start, and clean) --- + service_user = DEFAULTS["service_user"] + service_group = DEFAULTS["service_group"] + if do_install or do_start or do_clean: + service_user = args.service_user or (last_cfg.get("service_user") if last_cfg else None) \ + or prompt("Service user name", default=DEFAULTS["service_user"], yes_mode=yes) + service_group = args.service_group or (last_cfg.get("service_group") if last_cfg else None) \ + or prompt("Service group name", default=DEFAULTS["service_group"], yes_mode=yes) + + # --- Directories (needed by install and clean) --- + install_base = DEFAULTS["install_base"] + data_base = DEFAULTS["data_base"] + metadata_base = DEFAULTS["data_base"] + if do_install or do_clean: + install_base = args.install_dir or (last_cfg.get("install_base") if last_cfg else None) \ + or prompt("Install directory (base path for binaries, configs and logs)", default=DEFAULTS["install_base"], yes_mode=yes) + data_base_raw = args.data_dir or (last_cfg.get("data_base") if last_cfg else None) \ + or prompt( + "Data directory (hdds.datanode.dir; comma-separated or brace expansion e.g. /data/ozone{1..3})", + default=DEFAULTS["data_base"], yes_mode=yes, + ) data_base = parse_data_dirs(data_base_raw) if data_base_raw else (data_base_raw or DEFAULTS["data_base"]) metadata_base_raw = getattr(args, "metadata_dir", None) or (last_cfg.get("metadata_base") if last_cfg else None) or data_base metadata_base = parse_data_dirs(metadata_base_raw) if metadata_base_raw else (metadata_base_raw or data_base) - playbook = PLAYBOOKS_DIR / "stop_and_clean.yml" - extra_vars = {"cluster_mode": cluster_mode, "ssh_user": ssh_user, "install_base": install_base, "data_base": data_base, "metadata_base": metadata_base, "controller_logs_dir": str(LOGS_DIR)} - logger.info("Running stop and clean on cluster...") - else: - # Full install: resolve version, paths, service config, etc. - playbook = None # set later - extra_vars = None # set later - if not (stop_only or stop_and_clean): - # Resolve download base early for version selection + # --- Install-specific inputs --- + ozone_version = None + jdk_major = DEFAULTS["jdk_major"] + dl_url = DEFAULTS["dl_url"] + use_sudo = DEFAULTS["use_sudo"] + python_interpreter: Optional[str] = None + local_shared_path: Optional[str] = None + local_oz_dir: Optional[str] = None + + if do_install: dl_url = args.dl_url or (last_cfg.get("dl_url") if last_cfg else None) or DEFAULTS["dl_url"] ozone_version = args.version or (last_cfg.get("ozone_version") if last_cfg else None) if not ozone_version: @@ -659,40 +720,30 @@ def main(argv: List[str]) -> int: ozone_version = selected else: ozone_version = prompt("Ozone version (e.g., 2.1.0 | local)", default=DEFAULTS["ozone_version"], yes_mode=yes) - jdk_major = args.jdk_version if args.jdk_version is not None else ((last_cfg.get("jdk_major") if last_cfg else None)) - if jdk_major is None: - _jdk_val = prompt("JDK major (option: 17 or 21)", default=str(DEFAULTS["jdk_major"]), yes_mode=yes) + + jdk_val = args.jdk_version if args.jdk_version is not None else (last_cfg.get("jdk_major") if last_cfg else None) + if jdk_val is None: + _jdk_str = prompt("JDK major (option: 17 or 21)", default=str(DEFAULTS["jdk_major"]), yes_mode=yes) try: - jdk_major = int(str(_jdk_val)) if _jdk_val is not None else DEFAULTS["jdk_major"] + jdk_major = int(str(_jdk_str)) if _jdk_str is not None else DEFAULTS["jdk_major"] + except Exception: + jdk_major = DEFAULTS["jdk_major"] + else: + try: + jdk_major = int(jdk_val) except Exception: jdk_major = DEFAULTS["jdk_major"] - install_base = args.install_dir or (last_cfg.get("install_base") if last_cfg else None) \ - or prompt("Install directory (base directory path to store ozone binaries, configs and logs)", default=DEFAULTS["install_base"], yes_mode=yes) - data_base_raw = args.data_dir or (last_cfg.get("data_base") if last_cfg else None) \ - or prompt("Data directory (hdds.datanode.dir; base path(s), comma-separated or brace expansion e.g. /data/ozone{1..3})", default=DEFAULTS["data_base"], yes_mode=yes) - data_base = parse_data_dirs(data_base_raw) if data_base_raw else (data_base_raw or DEFAULTS["data_base"]) - metadata_base_raw = getattr(args, "metadata_dir", None) or (last_cfg.get("metadata_base") if last_cfg else None) or data_base - metadata_base = parse_data_dirs(metadata_base_raw) if metadata_base_raw else (metadata_base_raw or data_base) - service_user = args.service_user or (last_cfg.get("service_user") if last_cfg else None) \ - or prompt("Service user name ", default=DEFAULTS["service_user"], yes_mode=yes) - service_group = args.service_group or (last_cfg.get("service_group") if last_cfg else None) \ - or prompt("Service group name", default=DEFAULTS["service_group"], yes_mode=yes) - dl_url = args.dl_url or (last_cfg.get("dl_url") if last_cfg else None) or DEFAULTS["dl_url"] - use_sudo = (args.use_sudo or (last_cfg.get("use_sudo") if last_cfg else None) - or DEFAULTS["use_sudo"]) + use_sudo = (args.use_sudo or (last_cfg.get("use_sudo") if last_cfg else None) or DEFAULTS["use_sudo"]) - # Local specifics (single path to local build) - local_path = (getattr(args, "local_path", None) or (last_cfg.get("local_path") if last_cfg else None)) - local_shared_path = None - local_oz_dir = None + local_path_arg = getattr(args, "local_path", None) or (last_cfg.get("local_path") if last_cfg else None) if ozone_version and ozone_version.lower() == "local": candidate = None - if local_path: - candidate = Path(local_path).expanduser().resolve() + if local_path_arg: + candidate = Path(local_path_arg).expanduser().resolve() else: legacy_shared = (last_cfg.get("local_shared_path") if last_cfg else None) - legacy_dir = (last_cfg.get("local_ozone_dirname") if last_cfg else None) + legacy_dir = (last_cfg.get("local_ozone_dirname") if last_cfg else None) if legacy_shared and legacy_dir: candidate = Path(legacy_shared).expanduser().resolve() / legacy_dir @@ -712,10 +763,15 @@ def main(argv: List[str]) -> int: logger.warning("Invalid path. Expected an Ozone build directory with bin/ozone. Please try again.") local_shared_path = str(candidate.parent) - local_oz_dir = candidate.name - local_path = str(candidate) + local_oz_dir = candidate.name - # Build a human-friendly summary table of inputs before continuing + python_interpreter = args.python_interpreter or (last_cfg.get("python_interpreter") if last_cfg else None) + if python_interpreter: + logger.info(f"Using Python interpreter: {python_interpreter}") + else: + logger.info("Python interpreter will be auto-detected by playbook") + + # Summary confirmation before proceeding with install if use_master_worker: m_count = len(master_hosts) w_count = len(worker_hosts) @@ -728,73 +784,60 @@ def main(argv: List[str]) -> int: summary_host_rows = [("Hosts", f"{h_count} host{'s' if h_count != 1 else ''}")] summary_rows = summary_host_rows + [ - ("Cluster mode", cluster_mode), - ("Ozone version", str(ozone_version)), - ("JDK major", str(jdk_major)), - ("Install directory", str(install_base)), - ("Data directory (hdds.datanode.dir)", str(data_base)), + ("Actions", ", ".join(actions)), + ("Cluster mode", cluster_mode), + ("Ozone version", str(ozone_version)), + ("JDK major", str(jdk_major)), + ("Install directory", str(install_base)), + ("Data directory (hdds.datanode.dir)", str(data_base)), ("Metadata directory (ozone.metadata.dirs, etc.)", str(metadata_base)), - ("SSH user", str(ssh_user)), - ("SSH auth method", str(auth_method)) + ("SSH user", str(ssh_user)), + ("SSH auth method", str(auth_method)), ] if keyfile: summary_rows.append(("Key file", str(keyfile))) summary_rows.extend([ - ("Use sudo", str(bool(use_sudo))), - ("Service user", str(service_user)), + ("Use sudo", str(bool(use_sudo))), + ("Service user", str(service_user)), ("Service group", str(service_group)), ]) if ozone_version and str(ozone_version).lower() == "local": - summary_rows.append(("Local Ozone path", str(local_path or ""))) + summary_rows.append(("Local Ozone path", str(local_path_arg or local_shared_path or ""))) if not _confirm_summary(summary_rows, yes_mode=yes): logger.info("Aborted by user.") return 1 - python_interpreter = args.python_interpreter or (last_cfg.get("python_interpreter") if last_cfg else None) - if python_interpreter: - logger.info(f"Using Python interpreter: {python_interpreter}") - else: - logger.info("Python interpreter will be auto-detected by playbook") - - do_cleanup = False - if args.clean: - do_cleanup = True - else: - answer = prompt(f"Cleanup existing install at {install_base} (if present)? (y/N)", default="n", yes_mode=yes) - if str(answer).strip().lower().startswith("y"): - do_cleanup = True - - extra_vars = { - "cluster_mode": cluster_mode, - "install_base": install_base, - "data_base": data_base, - "metadata_base": metadata_base, - "jdk_major": jdk_major, - "service_user": service_user, - "service_group": service_group, - "dl_url": dl_url, + # --- Build extra_vars --- + extra_vars: Dict[str, Any] = { + "cluster_mode": cluster_mode, + "actions": actions, # JSON list → Ansible receives it as a list + "install_base": install_base, + "data_base": data_base, + "metadata_base": metadata_base, + "service_user": service_user, + "service_group": service_group, + "JAVA_MARKER": DEFAULTS["JAVA_MARKER"], + "ENV_MARKER": DEFAULTS["ENV_MARKER"], + "controller_logs_dir": str(LOGS_DIR), + } + if do_install: + extra_vars.update({ "ozone_version": ozone_version, - "start_after_install": True, - "use_sudo": bool(use_sudo), - "do_cleanup": bool(do_cleanup), - "JAVA_MARKER": DEFAULTS["JAVA_MARKER"], - "ENV_MARKER": DEFAULTS["ENV_MARKER"], - "controller_logs_dir": str(LOGS_DIR), - } + "jdk_major": jdk_major, + "dl_url": dl_url, + "use_sudo": bool(use_sudo), + }) if python_interpreter: extra_vars["ansible_python_interpreter"] = python_interpreter extra_vars["ansible_python_interpreter_discovery"] = "explicit" - if ozone_version and ozone_version.lower() == "local": - extra_vars.update({ - "local_shared_path": local_shared_path or "", - "local_ozone_dirname": local_oz_dir or "", - }) - playbook = PLAYBOOKS_DIR / "cluster.yml" + if ozone_version and str(ozone_version).lower() == "local": + extra_vars["local_shared_path"] = local_shared_path or "" + extra_vars["local_ozone_dirname"] = local_oz_dir or "" skip_s3g = bool(getattr(args, "skip_s3g", False)) skip_tags_list = ["ozone_smoke_s3g"] if skip_s3g else None - # Common: build inventory and run playbook (same for install, stop-only, stop-and-clean) + # --- Build inventory --- inventory_text = build_inventory( master_hosts=master_hosts if use_master_worker else None, worker_hosts=worker_hosts if use_master_worker else None, @@ -805,92 +848,73 @@ def main(argv: List[str]) -> int: skip_s3g=skip_s3g, ) ask_pass = auth_method == "password" and not password + playbook = PLAYBOOKS_DIR / "cluster.yml" - if stop_only or stop_and_clean: - extra_vars = _merge_extra_vars(extra_vars) - with tempfile.NamedTemporaryFile(mode="w", suffix=".ini", delete=False) as inv_f: - inv_f.write(inventory_text or "") - inv_path = Path(inv_f.name) - try: - with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as ev_f: - ev_f.write(json.dumps(extra_vars, indent=2)) - ev_path = Path(ev_f.name) - try: - return run_playbook(playbook, inv_path, ev_path, ask_pass=ask_pass, become=True, skip_tags=skip_tags_list, verbose=args.verbose) - finally: - try: - ev_path.unlink() - except Exception: - pass - finally: - try: - inv_path.unlink() - except Exception: - pass - - # Full install: persist config and run cluster playbook + # For non-install actions (stop / clean / start) we don't need to persist config; + # for install we persist so --resume can pick up where we left off. extra_vars = _merge_extra_vars(extra_vars) - with tempfile.TemporaryDirectory() as tdir: - inv_path = Path(tdir) / "hosts.ini" - ev_path = Path(tdir) / "vars.json" - inv_path.write_text(inventory_text or "", encoding="utf-8") - ev_path.write_text(json.dumps(extra_vars, indent=2), encoding="utf-8") + + if do_install: try: os.makedirs(LOGS_DIR, exist_ok=True) persisted_inv = LOGS_DIR / "last_inventory.ini" - persisted_ev = LOGS_DIR / "last_vars.json" + persisted_ev = LOGS_DIR / "last_vars.json" persisted_inv.write_text(inventory_text or "", encoding="utf-8") persisted_ev.write_text(json.dumps(extra_vars, indent=2), encoding="utf-8") - inv_path = persisted_inv - ev_path = persisted_ev - last_run = { - "host_file": host_file_path if host_file_path else None, - "hosts_raw": hosts_raw, + last_run: Dict[str, Any] = { + "host_file": host_file_path, + "hosts_raw": hosts_raw, "cluster_mode": cluster_mode, "ozone_version": ozone_version, - "jdk_major": jdk_major, + "jdk_major": jdk_major, "install_base": install_base, - "data_base": data_base, - "auth_method": auth_method, - "ssh_user": ssh_user, - "password": password if auth_method == "password" else None, - "keyfile": str(keyfile) if keyfile else None, + "data_base": data_base, + "metadata_base": metadata_base, + "auth_method": auth_method, + "ssh_user": ssh_user, + "password": password if auth_method == "password" else None, + "keyfile": str(keyfile) if keyfile else None, "service_user": service_user, "service_group": service_group, - "dl_url": dl_url, - "use_sudo": bool(use_sudo), - "local_shared_path": local_shared_path or "", + "dl_url": dl_url, + "use_sudo": bool(use_sudo), + "local_shared_path": local_shared_path or "", "local_ozone_dirname": local_oz_dir or "", - "python_interpreter": python_interpreter or "", + "python_interpreter": python_interpreter or "", + "actions": actions, } if use_master_worker: last_run["masters_raw"] = masters_raw last_run["workers_raw"] = workers_raw LAST_RUN_FILE.write_text(json.dumps(last_run, indent=2), encoding="utf-8") + inv_path = persisted_inv + ev_path = persisted_ev except Exception: - pass + # Fall back to temp files + inv_path = LOGS_DIR / "last_inventory.ini" + ev_path = LOGS_DIR / "last_vars.json" start_at = None use_tags = None - if args.resume: - if LAST_FAILED_FILE.exists(): - try: - # use first line (task name) - contents = LAST_FAILED_FILE.read_text(encoding="utf-8").splitlines() - start_at = contents[0].strip() if contents else None - # derive role tag if present - role_line = next((l for l in contents if l.startswith("# role:")), None) - if role_line: - role_name = role_line.split(":", 1)[1].strip() - if role_name: - use_tags = [role_name] - except Exception: - start_at = None - rc = run_playbook(playbook, inv_path, ev_path, ask_pass=ask_pass, become=True, start_at_task=start_at, tags=use_tags, skip_tags=skip_tags_list, verbose=args.verbose) + if args.resume and LAST_FAILED_FILE.exists(): + try: + contents = LAST_FAILED_FILE.read_text(encoding="utf-8").splitlines() + start_at = contents[0].strip() if contents else None + role_line = next((l for l in contents if l.startswith("# role:")), None) + if role_line: + role_name = role_line.split(":", 1)[1].strip() + if role_name: + use_tags = [role_name] + except Exception: + start_at = None + + rc = run_playbook(playbook, inv_path, ev_path, ask_pass=ask_pass, become=True, + start_at_task=start_at, tags=use_tags, skip_tags=skip_tags_list, + verbose=args.verbose) if rc != 0: return rc - # Successful completion: remove last_* persisted files so a fresh run starts clean + # Successful install: remove persisted last_* files so next run starts clean try: for f in LOGS_DIR.glob("last_*"): try: @@ -898,17 +922,45 @@ def main(argv: List[str]) -> int: except FileNotFoundError: pass except Exception: - # Best-effort cleanup; ignore failures pass except Exception: pass try: example_host = (master_hosts[0]["host"] if use_master_worker and master_hosts else hosts[0]["host"] if hosts else "HOSTNAME") - logger.info(f"To view process logs: ssh to the node and read {install_base}/current/logs/ozone-{service_user}-<process>-<host>.log " - f"(e.g., {install_base}/current/logs/ozone-{service_user}-recon-{example_host}.log)") + logger.info( + f"To view process logs: ssh to the node and read " + f"{install_base}/current/logs/ozone-{service_user}-<process>-<host>.log " + f"(e.g., {install_base}/current/logs/ozone-{service_user}-recon-{example_host}.log)" + ) except Exception: pass + + else: + # stop / clean / start — use temp files (no config to persist) + with tempfile.NamedTemporaryFile(mode="w", suffix=".ini", delete=False) as inv_f: + inv_f.write(inventory_text or "") + inv_path = Path(inv_f.name) + try: + with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as ev_f: + ev_f.write(json.dumps(extra_vars, indent=2)) + ev_path = Path(ev_f.name) + try: + rc = run_playbook(playbook, inv_path, ev_path, ask_pass=ask_pass, become=True, + skip_tags=skip_tags_list, verbose=args.verbose) + if rc != 0: + return rc + finally: + try: + ev_path.unlink() + except Exception: + pass + finally: + try: + inv_path.unlink() + except Exception: + pass + logger.info("All done.") return 0 diff --git a/playbooks/cluster.yml b/playbooks/cluster.yml index 4444243..6cf5f09 100644 --- a/playbooks/cluster.yml +++ b/playbooks/cluster.yml @@ -13,33 +13,63 @@ # See the License for the specific language governing permissions and # limitations under the License. +# ----------------------------------------------------------------------- +# Single unified playbook. All roles are gated by the 'actions' list var. +# +# Valid actions : clean | install | start | stop +# Inclusivity : 'install' implies 'start' +# 'clean' implies 'stop' first +# Exclusivity : 'start' and 'stop' cannot be combined +# +# Examples: +# actions: [install] → install + start +# actions: [start] → start only +# actions: [stop] → stop only +# actions: [clean] → stop + remove all dirs +# actions: [clean, install] → full clean reinstall +# ----------------------------------------------------------------------- + --- -- name: "Ozone Cluster Deployment" +- name: "Ozone Cluster" hosts: all gather_facts: false vars: - # Expect cluster_mode to be passed in (non-ha | ha). Fallback to non-ha. cluster_mode: "{{ cluster_mode | default('non-ha') }}" ha_enabled: "{{ cluster_mode == 'ha' }}" - # data_base_list: comma-separated data_base expanded to list (set in pre_tasks) + actions: "{{ actions | default(['install']) }}" + # Derived convenience booleans (evaluated once here, used in role when: clauses) + _do_stop: "{{ 'stop' in actions or 'clean' in actions }}" + _do_clean: "{{ 'clean' in actions }}" + _do_install: "{{ 'install' in actions }}" + _do_start: "{{ 'start' in actions or 'install' in actions }}" + pre_tasks: + - name: "Validate: 'start' and 'stop' cannot be combined" + fail: + msg: "Invalid actions: 'start' and 'stop' cannot be used together." + when: "'start' in actions and 'stop' in actions" + tags: ["always"] + - name: "Pre-install: Parse data_base into list (supports comma-separated dirs)" set_fact: data_base_list: "{{ ((data_base | default('')).split(',') | map('trim') | select | list) | default([data_base | default('/data/ozone')], true) }}" + when: _do_install | bool or _do_clean | bool - name: "Pre-install: Parse metadata_base into list (supports comma-separated dirs)" set_fact: metadata_base_list: "{{ ((metadata_base | default(data_base) | default('')).split(',') | map('trim') | select | list) | default([metadata_base | default(data_base) | default('/data/ozone')], true) }}" + when: _do_install | bool or _do_clean | bool - - name: "Pre-install: Gather facts" + - name: "Gather facts" setup: + when: _do_install | bool or _do_start | bool - name: "Pre-install: Install ACL package (needed for become_user when controller is macOS)" package: name: acl state: present - when: ansible_os_family in ['Debian', 'RedHat', 'Suse'] + when: _do_install | bool and ansible_os_family in ['Debian', 'RedHat', 'Suse'] become: true - name: "Pre-install: Ensure Ansible remote tmp exists" @@ -48,31 +78,48 @@ state: directory mode: "0700" owner: "{{ ansible_user_id | default(ansible_user) | default('ansible') }}" + when: _do_install | bool or _do_start | bool roles: - role: ozone_stop - tags: ["ozone_stop", "cleanup"] - when: (do_cleanup | default(false)) + tags: ["ozone_stop"] + when: _do_stop | bool + - role: ozone_cleanup - tags: ["cleanup"] - when: (do_cleanup | default(false)) + tags: ["ozone_cleanup"] + when: _do_clean | bool + - role: ozone_user tags: ["ozone_user"] + when: _do_install | bool + - role: ssh_bootstrap tags: ["ssh_bootstrap"] + when: _do_install | bool + - role: java tags: ["java"] + when: _do_install | bool + - role: ozone_layout tags: ["ozone_layout"] + when: _do_install | bool + - role: ozone_fetch tags: ["ozone_fetch"] + when: _do_install | bool + - role: ozone_config tags: ["ozone_config"] + when: _do_install | bool + - role: ozone_service tags: ["ozone_service"] + when: _do_start | bool + - name: "Ozone Cluster Smoke Test" - hosts: "{{ groups['om'] | list | first }}" + hosts: "{{ groups['om'] | list | first if ('install' in (actions | default([])) or 'start' in (actions | default([]))) else 'never' }}" gather_facts: false roles: - role: ozone_ui @@ -81,14 +128,14 @@ tags: ["ozone_smoke_cluster"] - name: "Ozone S3G Smoke Test" - hosts: "{{ (groups.get('s3g', []) | list)[0] if (groups.get('s3g', []) | length) > 0 else 'never' }}" + hosts: "{{ ((groups.get('s3g', []) | list)[0] if (groups.get('s3g', []) | length) > 0 else 'never') if ('install' in (actions | default([])) or 'start' in (actions | default([]))) else 'never' }}" gather_facts: false roles: - role: ozone_smoke_s3g tags: ["ozone_smoke_s3g"] - name: "Display UI Endpoints" - hosts: "{{ groups['om'] | list | first }}" + hosts: "{{ groups['om'] | list | first if ('install' in (actions | default([])) or 'start' in (actions | default([]))) else 'never' }}" gather_facts: false post_tasks: - name: "Build UI endpoints display lines" diff --git a/playbooks/stop.yml b/playbooks/stop.yml deleted file mode 100644 index 53a1ecd..0000000 --- a/playbooks/stop.yml +++ /dev/null @@ -1,22 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -# PURPOSE AND NONINFRINGEMENT. In no event shall the ASF be liable for any -# claim, damages or other liability. -# ---- -- name: "Stop Ozone cluster" - hosts: all - gather_facts: false - roles: - - role: ozone_stop - tags: ["ozone_stop"] diff --git a/playbooks/stop_and_clean.yml b/playbooks/stop_and_clean.yml deleted file mode 100644 index 1920614..0000000 --- a/playbooks/stop_and_clean.yml +++ /dev/null @@ -1,36 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -# PURPOSE AND NONINFRINGEMENT. In no event shall the ASF be liable for any -# claim, damages or other liability. -# ---- -- name: "Stop Ozone cluster and clean directories" - hosts: all - gather_facts: false - pre_tasks: - - name: "Parse data_base into list" - set_fact: - data_base_list: "{{ ((data_base | default('')).split(',') | map('trim') | select | list) | default([data_base | default('/data/ozone')], true) }}" - - - name: "Parse metadata_base into list" - set_fact: - metadata_base_list: "{{ ((metadata_base | default(data_base) | default('')).split(',') | map('trim') | select | list) | default([metadata_base | default(data_base) | default('/data/ozone')], true) }}" - - - name: "Gather facts" - setup: - - roles: - - role: ozone_stop - tags: ["ozone_stop", "cleanup"] - - role: ozone_cleanup - tags: ["cleanup"] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
