This is an automated email from the ASF dual-hosted git repository.

ssulav pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone-installer.git


The following commit(s) were added to refs/heads/master by this push:
     new bba6708  HDDS-14667. Add support for multiple disks ozone installation 
(#1)
bba6708 is described below

commit bba670874c23f1a773b55a23acadb709fe36d407
Author: Soumitra Sulav <[email protected]>
AuthorDate: Fri Feb 20 23:17:05 2026 +0530

    HDDS-14667. Add support for multiple disks ozone installation (#1)
---
 README.md                                      |  4 ++--
 ozone_installer.py                             | 21 ++++++++++++++++++---
 playbooks/cluster.yml                          |  5 +++++
 roles/cleanup/tasks/main.yml                   |  5 +++--
 roles/ozone_config/defaults/main.yml           |  1 +
 roles/ozone_config/templates/ozone-site.xml.j2 | 16 +++++++++-------
 roles/ozone_layout/tasks/main.yml              |  6 +-----
 roles/ozone_service/tasks/main.yml             |  4 ++--
 8 files changed, 41 insertions(+), 21 deletions(-)

diff --git a/README.md b/README.md
index 6a3e710..1905887 100644
--- a/README.md
+++ b/README.md
@@ -256,7 +256,7 @@ ANSIBLE_CONFIG=ansible.cfg ansible-playbook -i 
inventories/dev/hosts.ini playboo
 ### Directories
 
 - Install base (`install_base`, default `/opt/ozone`): where Ozone binaries 
and configs live. A `current` symlink points to the active version directory.
-- Data base (`data_base`, default `/data/ozone`): where Ozone writes on‑disk 
metadata and Datanode data (e.g., `ozone.metadata.dirs`, `hdds.datanode.dir`).
+- Data base (`data_base`, default `/data/ozone`): where Ozone writes on‑disk 
metadata and Datanode data (e.g., `ozone.metadata.dirs`, `hdds.datanode.dir`). 
Supports comma-separated multiple directories (e.g. 
`/data/ozone1,/data/ozone2`) or brace expansion (e.g. `/data/ozone{1..3}` → 
`/data/ozone1,/data/ozone2,/data/ozone3`); each path gets `dn`, `meta`, 
`data/om`, etc. subdirs in `ozone-site.xml`.
 
 ## Components and config mapping
 
@@ -265,7 +265,7 @@ ANSIBLE_CONFIG=ansible.cfg ansible-playbook -i 
inventories/dev/hosts.ini playboo
   - HA: first three hosts serve as OM and SCM sets; all hosts are DNs; first 
host is Recon.
 - `ozone-site.xml` is rendered from templates based on inventory groups:
   - `ozone.scm.names`, `ozone.scm.client.address`, `ozone.om.address` or HA 
service IDs
-  - `ozone.metadata.dirs`, `hdds.datanode.dir`, and related paths map to 
`data_base`
+  - `ozone.metadata.dirs`, `hdds.datanode.dir`, and related paths map to 
`data_base` (comma-separated dirs are expanded per property)
   - Replication is set to ONE if DN count < 3, otherwise THREE
 
 ## Optional: S3 Gateway (S3G) and smoke
diff --git a/ozone_installer.py b/ozone_installer.py
index 93c0084..c044ac9 100755
--- a/ozone_installer.py
+++ b/ozone_installer.py
@@ -85,7 +85,7 @@ def parse_args(argv: List[str]) -> argparse.Namespace:
     p.add_argument("-k", "--keyfile", help="SSH private key file (for 
--auth-method=key)")
     p.add_argument("-v", "--version", help="Ozone version (e.g., 2.0.0) or 
'local'")
     p.add_argument("-i", "--install-dir", help=f"Install root (default: 
{DEFAULTS['install_base']})")
-    p.add_argument("-d", "--data-dir", help=f"Data root (default: 
{DEFAULTS['data_base']})")
+    p.add_argument("-d", "--data-dir", help=f"Data root(s), comma-separated or 
brace expansion e.g. /data/ozone{{1..3}} (default: {DEFAULTS['data_base']})")
     p.add_argument("-s", "--start", action="store_true", help="Initialize and 
start after install")
     p.add_argument("-M", "--cluster-mode", choices=["non-ha", "ha"], 
help="Force cluster mode (default: auto by host count)")
     p.add_argument("-r", "--role-file", help="Role file (YAML) for HA mapping 
(optional)")
@@ -273,6 +273,20 @@ def expand_braces(expr: str) -> List[str]:
     pre, a, b, post = m.group(1), int(m.group(2)), int(m.group(3)), m.group(4)
     return [f"{pre}{i}{post}" for i in range(a, b + 1)]
 
+def parse_data_dirs(data_raw: Optional[str]) -> str:
+    """
+    Accepts comma-separated data dirs; each may contain brace expansion (e.g. 
/data/ozone{1..3}).
+    Returns comma-separated string of expanded paths.
+    """
+    if not data_raw:
+        return data_raw or ""
+    out = []
+    for token in data_raw.split(","):
+        token = token.strip()
+        expanded = expand_braces(token)
+        out.extend(expanded)
+    return ",".join(out)
+
 def parse_hosts(hosts_raw: Optional[str]) -> List[dict]:
     """
     Accepts comma-separated hosts; each may contain brace expansion.
@@ -500,8 +514,9 @@ def main(argv: List[str]) -> int:
             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 = args.data_dir or (last_cfg.get("data_base") if last_cfg else 
None) \
-        or prompt("Data directory (base directory path to store ozone metadata 
and data)", default=DEFAULTS["data_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 (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"])
 
     # Auth (before service user/group)
     auth_method = args.auth_method or (last_cfg.get("auth_method") if last_cfg 
else None) \
diff --git a/playbooks/cluster.yml b/playbooks/cluster.yml
index bef59f4..f67c45e 100644
--- a/playbooks/cluster.yml
+++ b/playbooks/cluster.yml
@@ -21,8 +21,13 @@
     # 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)
   pre_tasks:
 
+    - 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) }}"
+
     - name: "Pre-install: Gather facts"
       setup:
 
diff --git a/roles/cleanup/tasks/main.yml b/roles/cleanup/tasks/main.yml
index 5d41c8f..14eb003 100644
--- a/roles/cleanup/tasks/main.yml
+++ b/roles/cleanup/tasks/main.yml
@@ -58,10 +58,11 @@
         state: absent
       become: true
 
-    - name: "Remove data directory"
+    - name: "Remove data directories"
       file:
-        path: "{{ data_base }}"
+        path: "{{ item }}"
         state: absent
+      loop: "{{ data_base_list | default([data_base | default('/data/ozone')]) 
}}"
       become: true
 
 
diff --git a/roles/ozone_config/defaults/main.yml 
b/roles/ozone_config/defaults/main.yml
index 7473b5b..a527b6b 100644
--- a/roles/ozone_config/defaults/main.yml
+++ b/roles/ozone_config/defaults/main.yml
@@ -15,6 +15,7 @@
 
 ---
 install_base: "/opt/ozone"
+# data_base: single path or comma-separated paths (e.g. /data/ozone or 
/data/ozone1,/data/ozone2)
 data_base: "/data/ozone"
 CONFIG_DIR: ""   # if provided, can be used to feed additional properties via 
vars
 
diff --git a/roles/ozone_config/templates/ozone-site.xml.j2 
b/roles/ozone_config/templates/ozone-site.xml.j2
index f838f03..db8d458 100644
--- a/roles/ozone_config/templates/ozone-site.xml.j2
+++ b/roles/ozone_config/templates/ozone-site.xml.j2
@@ -16,6 +16,8 @@
 -->
 <configuration>
   <!-- Minimal Ozone site config; extend via group_vars if needed -->
+{% set _data_bases = data_base_list | default([data_base | 
default('/data/ozone')]) %}
+{% set _first_disk = _data_bases | first %}
 {% set _om_all = groups.get('om', [])| list %}
 {% set _scm_all = groups.get('scm', []) | list %}
 {% set _all_dn_count = groups.get('datanodes', []) | list | length %}
@@ -101,31 +103,31 @@
 {% endif %}
   <property>
     <name>ozone.metadata.dirs</name>
-    <value>{{ data_base }}/meta</value>
+    <value>{{ _first_disk }}/meta</value>
   </property>
   <property>
     <name>hdds.datanode.dir</name>
-    <value>{{ data_base }}/dn</value>
+    <value>{% for d in _data_bases %}{{ d }}/dn{% if not loop.last %},{% endif 
%}{% endfor %}</value>
   </property>
   <property>
     <name>dfs.container.ratis.datanode.storage.dir</name>
-    <value>{{ data_base }}/meta/dn</value>
+    <value>{{ _first_disk }}/meta/dn</value>
   </property>
   <property>
     <name>ozone.om.db.dirs</name>
-    <value>{{ data_base }}/data/om</value>
+    <value>{{ _first_disk }}/data/om</value>
   </property>
   <property>
     <name>ozone.om.ratis.snapshot.dir</name>
-    <value>{{ data_base }}/meta/om</value>
+    <value>{{ _first_disk }}/meta/om</value>
   </property>
   <property>
     <name>ozone.scm.db.dirs</name>
-    <value>{{ data_base }}/data/scm</value>
+    <value>{{ _first_disk }}/data/scm</value>
   </property>
   <property>
     <name>ozone.scm.datanode.id.dir</name>
-    <value>{{ data_base }}/meta/scm</value>
+    <value>{{ _first_disk }}/meta/scm</value>
   </property>
   <property>
     <name>ozone.scm.skip.bootstrap.validation</name>
diff --git a/roles/ozone_layout/tasks/main.yml 
b/roles/ozone_layout/tasks/main.yml
index 5a5f0d8..43c5c44 100644
--- a/roles/ozone_layout/tasks/main.yml
+++ b/roles/ozone_layout/tasks/main.yml
@@ -21,11 +21,7 @@
     owner: "{{ service_user }}"
     group: "{{ service_group }}"
     mode: "0755"
-  loop:
-    - "{{ install_base }}"
-    - "{{ data_base }}"
-    - "{{ data_base }}/dn"
-    - "{{ data_base }}/meta"
+  loop: "{{ [install_base] + (data_base_list | default([data_base | 
default('/data/ozone')]) | product(['', '/dn', '/meta']) | map('join', '') | 
list) }}"
   become: true
 
 - name: "Ensure OZONE_HOME and PATH are in profile.d/ozone.sh"
diff --git a/roles/ozone_service/tasks/main.yml 
b/roles/ozone_service/tasks/main.yml
index b4ce240..8746028 100644
--- a/roles/ozone_service/tasks/main.yml
+++ b/roles/ozone_service/tasks/main.yml
@@ -27,7 +27,7 @@
         - name: "Initialize first SCM"
           command: "ozone scm --init"
           args:
-            creates: "{{ data_base }}/meta/scm"
+            creates: "{{ (data_base_list | first) | default(data_base) 
}}/meta/scm"
           when: (groups['scm'] | length > 0) and (inventory_hostname == 
groups['scm'][0])
           register: scm_init_first
           failed_when: scm_init_first.rc != 0
@@ -41,7 +41,7 @@
         - name: "Initialize first OM"
           command: "ozone om --init"
           args:
-            creates: "{{ data_base }}/meta/om"
+            creates: "{{ (data_base_list | first) | default(data_base) 
}}/meta/om"
           when: (groups['om'] | length > 0) and (inventory_hostname == 
groups['om'][0])
           register: om_init_first
           failed_when: om_init_first.rc != 0


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to