From 2a2e191c4c752e7497f32535bb8523cfae19edc0 Mon Sep 17 00:00:00 2001 From: Gorjan Todorovski Date: Thu, 11 Sep 2025 19:51:42 +0200 Subject: [PATCH] Add role to validate minimum free disk space before installation --- .gitignore | 3 + config-rac-db.yml | 15 +++ roles/db-create/tasks/main.yml | 17 +++ roles/disk_space_check/defaults/main.yml | 32 ++++++ roles/disk_space_check/tasks/check_path.yml | 104 +++++++++++++++++++ roles/disk_space_check/tasks/main.yml | 62 +++++++++++ roles/disk_space_check/vars/requirements.yml | 70 +++++++++++++ roles/gi-setup/tasks/gi-install.yml | 27 +++++ roles/gi-setup/tasks/main.yml | 17 ++- roles/patch/tasks/main.yml | 16 +++ roles/rac-gi-setup/tasks/rac-gi-install.yml | 25 +++++ roles/rdbms-setup/tasks/main.yml | 17 +++ roles/swlib/tasks/main.yml | 16 +++ 13 files changed, 420 insertions(+), 1 deletion(-) create mode 100644 roles/disk_space_check/defaults/main.yml create mode 100644 roles/disk_space_check/tasks/check_path.yml create mode 100644 roles/disk_space_check/tasks/main.yml create mode 100644 roles/disk_space_check/vars/requirements.yml diff --git a/.gitignore b/.gitignore index a7c0ee7ba..f35f95972 100644 --- a/.gitignore +++ b/.gitignore @@ -29,5 +29,8 @@ secrets.yml secrets/ terraform/*sh-key* vars/ +!roles/*/vars/ vault_password_file terraform/terraform.tfvars +roles/disk_space_check/molecule/* +roles/disk_space_check/molecule/ diff --git a/config-rac-db.yml b/config-rac-db.yml index 6e6cbfcec..6b750638e 100644 --- a/config-rac-db.yml +++ b/config-rac-db.yml @@ -29,6 +29,21 @@ - hosts: dbasm[0] tasks: + # Ensure common defaults are present when included via include_role (Ansible 2.9) + - name: Load common defaults (fallback) + include_vars: + dir: "roles/common/defaults" + when: + - oracle_base is not defined or oracle_home is not defined or grid_home is not defined + tags: rac-db + + - name: Perform disk space checks for RAC database + include_role: + name: disk_space_check + vars: + disk_check_type: rac + tags: rac-db,disk_space_check + - include_role: name: db-create tasks_from: rac-db-create.yml diff --git a/roles/db-create/tasks/main.yml b/roles/db-create/tasks/main.yml index 815818055..7549a85f9 100644 --- a/roles/db-create/tasks/main.yml +++ b/roles/db-create/tasks/main.yml @@ -13,6 +13,23 @@ # limitations under the License. --- + +# Ensure common defaults are present when included via include_role (Ansible 2.9) +- name: Load common defaults (fallback) + include_vars: + dir: "{{ role_path }}/../common/defaults" + when: + - oracle_base is not defined or oracle_home is not defined or grid_home is not defined + tags: db-create + +# Check disk space before creating database +- name: db-create | Validate disk space for database creation + include_role: + name: disk_space_check + vars: + disk_check_type: db + tags: disk_space_check,db-create + - name: Test whether pmon process by same name already exists shell: "set -o pipefail; ps -ef | ( grep pmon || true ) | ( grep -i {{ db_name }} || true ) | ( grep -v grep || true ) | wc -l" changed_when: false diff --git a/roles/disk_space_check/defaults/main.yml b/roles/disk_space_check/defaults/main.yml new file mode 100644 index 000000000..3fc4dbee0 --- /dev/null +++ b/roles/disk_space_check/defaults/main.yml @@ -0,0 +1,32 @@ +--- +# Simplified defaults for disk space checking + +# Core behavior controls +disk_space_fail_on_insufficient: true +disk_space_warning_threshold_percent: 80 +disk_space_critical_threshold_percent: 90 +disk_space_show_warnings: true + +# Default space requirements (MB) - organized by component +disk_space_defaults: + oracle: + home: 10240 # 10GB + base: 5120 # 5GB + swlib: 19883 # 20GB + unzip: 10240 # 10GB + gi: + home: 15360 # 15GB + base: 5120 # 5GB + unzip: 10240 # 10GB + rac: + grid: 20480 # 20GB + oracle: 15360 # 15GB + unzip: 15360 # 15GB + patch: + home: 5120 # 5GB + extract: 10240 # 10GB + temp: 2048 # 2GB + db: + data: 20480 # 20GB + recovery: 10240 # 10GB + config: 512 # 512MB diff --git a/roles/disk_space_check/tasks/check_path.yml b/roles/disk_space_check/tasks/check_path.yml new file mode 100644 index 000000000..b0ff722f0 --- /dev/null +++ b/roles/disk_space_check/tasks/check_path.yml @@ -0,0 +1,104 @@ +--- +# Disk space validation for a specific path with proper delegation support +- name: Gather filesystem facts on target host + setup: + gather_subset: + - "!all" + - "!min" + - "mounts" + delegate_to: "{{ disk_check_host | default(inventory_hostname) }}" + delegate_facts: true + tags: disk_space_check + +- name: Get mounts from target host + set_fact: + target_host: "{{ disk_check_host | default(inventory_hostname) }}" + host_mounts: "{{ hostvars[disk_check_host | default(inventory_hostname)].ansible_mounts | default([]) }}" + tags: disk_space_check + +- name: Initialize mount tracking + set_fact: + target_mount: null + target_mount_len: 0 + tags: disk_space_check + +- name: Find best matching mount point (longest prefix match) + set_fact: + target_mount: "{{ item }}" + target_mount_len: "{{ item.mount | length }}" + loop: "{{ host_mounts }}" + when: + - (check_path | string).startswith(item.mount | string) + - (item.mount | length) > (target_mount_len | int) + tags: disk_space_check + +- name: Fail if no mount found + fail: + msg: "No mounted filesystem found for path {{ check_path }} on {{ target_host }}" + when: target_mount is none or target_mount is not defined + tags: disk_space_check + +- name: Calculate available space + set_fact: + available_mb: "{{ ((target_mount.size_available | default(0) | int) / 1048576) | int }}" + required_mb: "{{ required_mb | default(0) | int }}" + mount_point: "{{ target_mount.mount }}" + used_percent: "{{ (100 - ((target_mount.size_available | default(0) | float) / (target_mount.size_total | default(1) | float) * 100)) | int }}" + # Pre-calculate shortfall as integer to avoid type issues + shortfall_mb: "{{ ( (required_mb | default(0) | int) - ( ((target_mount.size_available | default(0) | int) / 1048576) | int ) ) }}" + changed_when: false + tags: disk_space_check + +- name: Display disk space check details (diagnostic) + debug: + msg: + - "Checking: {{ check_description }}" + - "Path: {{ check_path }}" + - "Mount: {{ mount_point }}" + - "Required: {{ required_mb }} MB" + - "Available: {{ available_mb }} MB" + - "Used: {{ used_percent }}%" + - "Status: {{ 'PASS' if (available_mb | int) >= (required_mb | int) else 'FAIL' }}" + verbosity: 1 + when: + - disk_space_show_warnings | default(true) | bool + - (available_mb | int) >= (required_mb | int) # Only show diagnostic info on success + tags: disk_space_check + +- name: Display disk space failure warning + debug: + msg: + - "WARNING: Insufficient disk space for {{ check_description }}" + - "Path: {{ check_path }} (Mount: {{ mount_point }})" + - "Required: {{ required_mb }} MB, Available: {{ available_mb }} MB" + - "Shortfall: {{ [((required_mb | int) - (available_mb | int)), 0] | max }} MB" + verbosity: 0 + when: + - disk_space_show_warnings | default(true) | bool + - (available_mb | int) < (required_mb | int) + - not (disk_space_fail_on_insufficient | default(true) | bool) # Only warn if not failing + tags: disk_space_check + +- name: Assert sufficient disk space + assert: + that: + - (available_mb | int) >= (required_mb | int) + fail_msg: | + Insufficient disk space for {{ check_description }} + Path: {{ check_path }} + Mount: {{ mount_point }} + Required: {{ required_mb }} MB + Available: {{ available_mb }} MB + Shortfall: {{ [shortfall_mb | int, 0] | max }} MB + success_msg: "Sufficient disk space for {{ check_description }} ({{ available_mb }} MB available)" + when: disk_space_fail_on_insufficient | default(true) | bool + tags: disk_space_check + +- name: Warn on high usage + debug: + msg: "WARNING: Mount {{ mount_point }} is {{ used_percent }}% full (threshold: {{ disk_space_warning_threshold_percent | default(80) }}%)" + verbosity: 0 + when: + - (used_percent | int) >= (disk_space_warning_threshold_percent | default(80) | int) + - disk_space_show_warnings | default(true) | bool + tags: disk_space_check diff --git a/roles/disk_space_check/tasks/main.yml b/roles/disk_space_check/tasks/main.yml new file mode 100644 index 000000000..dfce03fed --- /dev/null +++ b/roles/disk_space_check/tasks/main.yml @@ -0,0 +1,62 @@ +--- +# Disk space check role - main orchestration + +# Load common role defaults as a fallback (handles include_role without meta deps on 2.9) +- name: Load common defaults (fallback) + include_vars: + dir: "{{ role_path }}/../common/defaults" + when: + - oracle_base is not defined or oracle_home is not defined or grid_home is not defined + tags: disk_space_check + +# Load disk space requirements from vars directory +- name: Load disk space requirements + include_vars: "{{ role_path }}/vars/requirements.yml" + when: disk_space_requirements is not defined + tags: disk_space_check + +- name: Validate required variables for check type + assert: + that: + - oracle_ver is defined + - (disk_check_type != 'oracle_home') or (oracle_home is defined and oracle_base is defined) + - (disk_check_type != 'gi') or (grid_home is defined and oracle_base is defined) + - (disk_check_type != 'rac') or (grid_home is defined and oracle_home is defined) + - (disk_check_type != 'patch') or (grid_home is defined or oracle_home is defined) + - (disk_check_type != 'db') or ((ora_disk_management | default('fs') | lower) != 'fs') or (oracle_home is defined) + fail_msg: >- + Missing required variables for disk_check_type {{ disk_check_type }}. Ensure 'common' defaults are loaded + (role dependency) or include roles/common/defaults via include_vars. + when: + - disk_check_type is defined + - disk_check_type in ['oracle_home', 'gi', 'rac', 'patch', 'db'] + tags: disk_space_check + +- name: Validate disk space for component + include_tasks: check_path.yml + vars: + check_path: "{{ requirement.path }}" + required_mb: "{{ requirement.mb }}" + check_description: "{{ requirement.desc }}" + loop: "{{ disk_space_requirements[disk_check_type] | default([]) }}" + loop_control: + loop_var: requirement + when: + - disk_check_type is defined + - disk_space_requirements is defined + - disk_space_requirements[disk_check_type] is defined + - (disk_check_type != 'db') or ((ora_disk_management | default('fs') | lower) == 'fs') + - (disk_check_type != 'db') or (oracle_home is defined) + - requirement.when | default(true) | bool + tags: disk_space_check + +- name: Validate specific path if provided + include_tasks: check_path.yml + vars: + check_path: "{{ disk_check_path }}" + required_mb: "{{ disk_check_required_mb | default(disk_space_minimum_mb | default(1024)) }}" + check_description: "{{ disk_check_description | default('custom path') }}" + when: + - disk_check_path is defined + - disk_check_type is not defined + tags: disk_space_check diff --git a/roles/disk_space_check/vars/requirements.yml b/roles/disk_space_check/vars/requirements.yml new file mode 100644 index 000000000..4c3ea6029 --- /dev/null +++ b/roles/disk_space_check/vars/requirements.yml @@ -0,0 +1,70 @@ +--- +# Consolidated disk space requirements for all Oracle components +# Uses safe concatenation and defaults to avoid undefined variable errors +disk_space_requirements: + # Oracle RDBMS installation + oracle_home: + - path: "{{ oracle_home }}" + mb: "{{ disk_space_oracle_home_mb | default(disk_space_defaults.oracle.home) }}" + desc: "Oracle Home" + - path: "{{ oracle_base }}" + mb: "{{ disk_space_oracle_base_mb | default(disk_space_defaults.oracle.base) }}" + desc: "Oracle Base" + + # Software library staging + swlib: + - path: "{{ swlib_path }}" + mb: "{{ disk_space_swlib_mb | default(disk_space_defaults.oracle.swlib) }}" + desc: "Software library" + - path: "{{ swlib_unzip_path }}" + mb: "{{ disk_space_unzip_mb | default(disk_space_defaults.oracle.unzip) }}" + desc: "Software extraction" + + # Patching operations + patch: + - path: "{{ grid_home if gi_install else oracle_home }}" + mb: "{{ disk_space_patch_home_mb | default(disk_space_defaults.patch.home) }}" + desc: "Patch target" + - path: "{{ swlib_unzip_path }}" + mb: "{{ disk_space_patch_extract_mb | default(disk_space_defaults.patch.extract) }}" + desc: "Patch extraction" + - path: "/tmp" + mb: "{{ disk_space_patch_temp_mb | default(disk_space_defaults.patch.temp) }}" + desc: "Patch temporary space" + + # Grid Infrastructure + gi: + - path: "{{ grid_home }}" + mb: "{{ disk_space_gi_home_mb | default(disk_space_defaults.gi.home) }}" + desc: "Grid Infrastructure Home" + - path: "{{ oracle_base }}" + mb: "{{ disk_space_gi_base_mb | default(disk_space_defaults.gi.base) }}" + desc: "GI Oracle Base" + - path: "{{ swlib_unzip_path }}" + mb: "{{ disk_space_gi_unzip_mb | default(disk_space_defaults.gi.unzip) }}" + desc: "GI extraction" + + # RAC installation + rac: + - path: "{{ grid_home }}" + mb: "{{ disk_space_rac_grid_mb | default(disk_space_defaults.rac.grid) }}" + desc: "RAC Grid Home" + - path: "{{ oracle_home }}" + mb: "{{ disk_space_rac_oracle_mb | default(disk_space_defaults.rac.oracle) }}" + desc: "RAC Database Home" + - path: "{{ swlib_unzip_path }}" + mb: "{{ disk_space_rac_unzip_mb | default(disk_space_defaults.rac.unzip) }}" + desc: "RAC extraction" + + # Database creation + db: + - path: "{{ data_destination }}" + mb: "{{ disk_space_db_data_mb | default(disk_space_defaults.db.data) }}" + desc: "Database datafiles" + - path: "{{ reco_destination }}" + mb: "{{ disk_space_db_recovery_mb | default(disk_space_defaults.db.recovery) }}" + desc: "Fast Recovery Area" + - path: "{{ oracle_home }}/dbs" + mb: "{{ disk_space_db_config_mb | default(disk_space_defaults.db.config) }}" + desc: "Database config files" + # Always a filesystem path under ORACLE_HOME diff --git a/roles/gi-setup/tasks/gi-install.yml b/roles/gi-setup/tasks/gi-install.yml index a5a376a73..e39ae53ac 100644 --- a/roles/gi-setup/tasks/gi-install.yml +++ b/roles/gi-setup/tasks/gi-install.yml @@ -13,6 +13,14 @@ # limitations under the License. --- +# Ensure common defaults are present when included via include_role (Ansible 2.9) +- name: Load common defaults (fallback) + include_vars: + dir: "{{ role_path }}/../common/defaults" + when: + - oracle_base is not defined or oracle_home is not defined or grid_home is not defined + tags: gi-setup + - name: gi-install | Set facts set_fact: install_unzip_path: "{{ grid_home }}" @@ -37,6 +45,16 @@ verbosity: 0 tags: gi-setup +- name: gi-install | Check disk space before patch extraction + include_role: + name: disk_space_check + vars: + disk_check_path: "{{ swlib_unzip_path }}" + disk_check_required_mb: "{{ disk_space_defaults.gi.unzip }}" + disk_check_description: "Space for GI patch extraction" + when: gi_patches is defined and gi_patches | length > 0 + tags: gi-setup,disk_space_check + - name: gi-install | Unzip GI patch # Using the "shell" module instead of "unarchive" for unzip performance shell: | @@ -56,6 +74,15 @@ when: item.release == oracle_rel and item.category == 'RU' tags: gi-setup,rel-patch +- name: gi-install | Check disk space before GI software extraction + include_role: + name: disk_space_check + vars: + disk_check_path: "{{ install_unzip_path }}" + disk_check_required_mb: "{{ disk_space_defaults.gi.home }}" + disk_check_description: "Space for GI software extraction" + tags: gi-setup,disk_space_check + - name: gi-install | Unzipping GI software become: true become_user: "{{ grid_user }}" diff --git a/roles/gi-setup/tasks/main.yml b/roles/gi-setup/tasks/main.yml index e7c936ad4..24083fda6 100644 --- a/roles/gi-setup/tasks/main.yml +++ b/roles/gi-setup/tasks/main.yml @@ -13,8 +13,23 @@ # limitations under the License. --- + +- name: Load common defaults (fallback) + include_vars: + dir: "{{ role_path }}/../common/defaults" + when: + - oracle_base is not defined or oracle_home is not defined or grid_home is not defined + tags: gi-setup + +- name: Perform disk space checks for Grid Infrastructure + include_role: + name: disk_space_check + vars: + disk_check_type: gi + tags: gi-setup,disk_space_check + - name: Check if software is already installed - shell: cat "{{ oracle_inventory }}/ContentsXML/inventory.xml" 2>&1 | ( grep -w {{ grid_home }} || true ) | wc -l + shell: 'set -o pipefail; cat "{{ oracle_inventory }}/ContentsXML/inventory.xml" 2>&1 | ( grep -w {{ grid_home }} || true ) | wc -l' register: existing_dbhome become: true become_user: root diff --git a/roles/patch/tasks/main.yml b/roles/patch/tasks/main.yml index dc1c98104..26bd6250f 100644 --- a/roles/patch/tasks/main.yml +++ b/roles/patch/tasks/main.yml @@ -13,6 +13,22 @@ # limitations under the License. --- + +# Ensure common defaults are present when included via include_role (Ansible 2.9) +- name: Load common defaults (fallback) + include_vars: + dir: "{{ role_path }}/../common/defaults" + when: + - oracle_base is not defined or oracle_home is not defined or grid_home is not defined + +# Check disk space before applying patches +- name: patch | Validate disk space for patching operations + include_role: + name: disk_space_check + vars: + disk_check_type: patch + tags: disk_space_check,patch + - name: Create OCM response file script: expect_rsp.sh {{ oracle_base }} {{ oracle_home }} {{ swlib_unzip_path }} args: diff --git a/roles/rac-gi-setup/tasks/rac-gi-install.yml b/roles/rac-gi-setup/tasks/rac-gi-install.yml index 4eb0391d9..ad241a213 100644 --- a/roles/rac-gi-setup/tasks/rac-gi-install.yml +++ b/roles/rac-gi-setup/tasks/rac-gi-install.yml @@ -13,6 +13,21 @@ # limitations under the License. --- + +- name: rac-gi-install | Load common defaults (fallback) + include_vars: + dir: "{{ role_path }}/../common/defaults" + when: + - oracle_base is not defined or oracle_home is not defined or grid_home is not defined + tags: rac-gi + +- name: rac-gi-install | Perform disk space checks for RAC Grid Infrastructure + include_role: + name: disk_space_check + vars: + disk_check_type: rac + tags: rac-gi,disk_space_check + - name: rac-gi-install | Set facts set_fact: install_unzip_path: "{{ grid_home }}" @@ -28,6 +43,16 @@ verbosity: 0 tags: rac-gi +- name: rac-gi-install | Check disk space before patch extraction + include_role: + name: disk_space_check + vars: + disk_check_path: "{{ swlib_unzip_path }}" + disk_check_required_mb: "{{ disk_space_defaults.rac.unzip }}" + disk_check_description: "Space for RAC GI patch extraction" + when: gi_patches is defined and gi_patches | length > 0 + tags: rac-gi,disk_space_check + - name: rac-gi-install | Unzip GI patch # Using the "shell" module instead of "unarchive" for unzip performance shell: | diff --git a/roles/rdbms-setup/tasks/main.yml b/roles/rdbms-setup/tasks/main.yml index be0c34f30..f854b1d20 100644 --- a/roles/rdbms-setup/tasks/main.yml +++ b/roles/rdbms-setup/tasks/main.yml @@ -13,6 +13,23 @@ # limitations under the License. --- + +# Ensure common defaults are present when included via include_role (Ansible 2.9) +- name: Load common defaults (fallback) + include_vars: + dir: "{{ role_path }}/../common/defaults" + when: + - oracle_base is not defined or oracle_home is not defined or grid_home is not defined + tags: rdbms-setup + +# Check disk space before Oracle RDBMS installation +- name: rdbms-setup | Validate disk space requirements + include_role: + name: disk_space_check + vars: + disk_check_type: oracle_home + tags: disk_space_check,rdbms-setup + - name: Check if software is already installed shell: cat "{{ oracle_inventory }}/ContentsXML/inventory.xml" 2>&1 | ( grep -w {{ oracle_home }} || true ) | wc -l register: existing_dbhome diff --git a/roles/swlib/tasks/main.yml b/roles/swlib/tasks/main.yml index e109f4c2a..a5425d8fa 100644 --- a/roles/swlib/tasks/main.yml +++ b/roles/swlib/tasks/main.yml @@ -13,6 +13,22 @@ # limitations under the License. --- +# Ensure common defaults are present when included via include_role (Ansible 2.9) +- name: Load common defaults (fallback) + include_vars: + dir: "{{ role_path }}/../common/defaults" + when: + - oracle_base is not defined or oracle_home is not defined or grid_home is not defined + tags: swlib + +# Check disk space before creating software library +- name: swlib | Check disk space requirements + include_role: + name: disk_space_check + vars: + disk_check_type: swlib + tags: disk_space_check + - name: swlib | setup become: true become_user: root