deploy/ansible/playbook_00_validate_parameters.yaml (641 lines of code) (raw):

# Copyright (c) Microsoft Corporation. # Licensed under the MIT License. --- # /*---------------------------------------------------------------------------8 # | | # | Validate the parameters | # | | # +------------------------------------4--------------------------------------*/ - hosts: localhost name: "0.0 Validations" gather_facts: true vars_files: - vars/ansible-input-api.yaml # API Input template with defaults # -------------------------------------+---------------------------------------8 # # Build the list of tasks to be executed in order here. # # -------------------------------------+---------------------------------------8 tasks: - name: "0.0 Validations - Show SDAF Version" ansible.builtin.debug: msg: - "SDAF Version: {{ SDAF_Version }}" tags: - always - name: "0.0 Validations - Ensure jmespath is installed on the controller" ansible.builtin.pip: name: jmespath state: present tags: - always - name: "0.0 Validations - Ensure netaddr is installed in the controller" ansible.builtin.pip: name: netaddr state: present tags: - always # - name: "0.0 Validations - ensure credssp is installed in the controller" # ansible.builtin.pip: # name: 'pywinrm[credssp]' # state: present # failed_when: false - name: "0.0 Validations - Create Progress folder" ansible.builtin.file: path: "{{ _workspace_directory }}/.progress" state: directory mode: 0755 tags: - always - name: "0.0 Validations - Remove os-install-done flag" ansible.builtin.file: path: "{{ _workspace_directory }}/.progress/validation-done" state: absent tags: - always # -------------------------------------+---------------------------------------8 # # Ensure cluster determining parameters are present # # -------------------------------------+---------------------------------------8 - name: "Backward Compatibility - Check required Database HA variables " ansible.builtin.set_fact: database_high_availability: "{{ db_high_availability | default(false) }}" when: - db_high_availability is defined - database_high_availability is not defined tags: - always - name: "0.0 Validations - Check required variables are present and not empty" ansible.builtin.assert: that: - item_to_check.parameter is defined # Has the variable been defined - item_to_check.parameter | type_debug != 'NoneType' # Is the variable not empty" - item_to_check.parameter | trim | length > 1 fail_msg: "{{ item_to_check.error }}" loop: - { parameter: "database_high_availability", error: "database_high_availability has no value assigned" } - { parameter: "database_cluster_type", error: "database_cluster_type has no value assigned" } - { parameter: "scs_high_availability", error: "scs_high_availability has no value assigned" } - { parameter: "scs_cluster_type", error: "scs_cluster_type has no value assigned" } - { parameter: "use_msi_for_clusters", error: "use_msi_for_clusters has no value assigned" } - { parameter: "platform", error: "platform has no value assigned" } loop_control: loop_var: item_to_check tags: - always # -------------------------------------+---------------------------------------8 # # Show parameters used for cluster aware coding # # -------------------------------------+---------------------------------------8 - name: "Cluster aware code..." ansible.builtin.debug: msg: # Best method for formatting output with Azure Devops Logs - "database_high_availability: {{ database_high_availability }}" - "database_cluster_type: {{ database_cluster_type }}" - "scs_high_availability: {{ scs_high_availability }}" - "scs_cluster_type: {{ scs_cluster_type }}" - "use_msi_for_clusters: {{ use_msi_for_clusters }}" - "platform: {{ platform | upper }}" verbosity: 2 tags: - always # -------------------------------------+ # Fencing support is only needed when: # database_high_availability is true AND database_cluster_type is AFA (Azure Fencing Agent) # OR # scs_high_availability is true AND scs_cluster_type is AFA (Azure Fencing Agent) # # - name: fencing # block: # # - name: "0.0 Validations - Initialize tier" # # ansible.builtin.set_fact: # # tier: fencing # - name: "0.0 Validations - Retrieve the Fencing SPN details" # ansible.builtin.include_role: # name: roles-misc/0.2-kv-secrets # when: platform != "ORACLE" # tags: # - kv-secrets # when: (database_high_availability and database_cluster_type == "AFA") or # (scs_high_availability and scs_cluster_type == "AFA") - name: "0.0 Validations - Retrieve the Fencing SPN details" ansible.builtin.include_role: name: roles-misc/0.2-kv-secrets vars: operation: fencing when: - (database_high_availability and database_cluster_type == "AFA") or (scs_high_availability and scs_cluster_type == "AFA") - platform != "ORACLE" tags: - always - kv-secrets # -------------------------------------+ - name: "0.0 Validations - Retrieve the domain passwords" ansible.builtin.include_role: name: roles-misc/0.1-passwords tasks_from: windows.yaml public: true tags: - always - 0.1-win-passwords when: - platform == "SQLSERVER" - name: "0.0 Validations - Retrieve the storage account details" ansible.builtin.include_role: name: roles-misc/0.3.sap-installation-media-storage-details public: true tags: - always - kv-sap-installation-media-storage-details - name: "0.0 Validations - Check required variables are present and not empty" ansible.builtin.assert: that: - item_to_check.parameter is defined # Has the variable been defined - item_to_check.parameter | type_debug != 'NoneType' # Is the variable not empty" - item_to_check.parameter | trim | length > 1 fail_msg: "{{ item_to_check.error }}" loop: - { parameter: "bom_base_name", error: "Please provide the Bill of materials name in bom_base_name parameter", } - { parameter: "kv_name", error: "Please provide the workload zone key vault name in kv_name parameter", } - { parameter: "sap_fqdn", error: "Please provide the SAP fqdn in in sap_fqdn parameter", } - { parameter: "sap_sid", error: "Please provide the SAP SID in sap_sid parameter", } - { parameter: "secret_prefix", error: "Please provide the Key Vault secret prefix in secret_prefix parameter", } - { parameter: "sapbits_location_base_path", error: "Please provide the sapbits_location_base_path in the deployer key vault", } loop_control: loop_var: item_to_check tags: - always - name: "0.0 Validations - Check required SCS HA variables are present and not empty" ansible.builtin.assert: that: - item_to_check.parameter is defined # Has the variable been defined - item_to_check.parameter | type_debug != 'NoneType' # Is the variable not empty" - item_to_check.parameter | trim | length > 1 fail_msg: "{{ item_to_check.error }}" loop: - { parameter: "NFS_provider", error: "A highly available SCS deployment requires that NFS_Provider provided", } - { parameter: "sap_mnt", error: "A highly available SCS deployment requires a shared sap_mnt is provided", } - { parameter: "scs_lb_ip", error: "A highly available SCS deployment requires that scs_lb_ip is provided", } - { parameter: "ecs_lb_ip", error: "A highly available SCS deployment requires that ers_lb_ip is provided", } loop_control: loop_var: item_to_check when: - scs_high_availability - platform != "SQLSERVER" tags: - always - name: "0.0 Validations - Check required SCS HA fencing variables are present and not empty" ansible.builtin.assert: that: - item_to_check.parameter is defined # Has the variable been defined - item_to_check.parameter | type_debug != 'NoneType' # Is the variable not empty" - item_to_check.parameter | trim | length > 1 fail_msg: "{{ item_to_check.error }}" loop: - { parameter: "fencing_spn_client_id", error: "A highly available SCS deployment requires that an SPN is defined for the fencing agent", } - { parameter: "fencing_spn_client_pwd", error: "A highly available SCS deployment requires that an SPN is defined for the fencing agent", } - { parameter: "fencing_spn_subscription_id", error: "A highly available SCS deployment requires that an SPN is defined for the fencing agent", } - { parameter: "fencing_spn_tenant_id", error: "A highly available SCS deployment requires that an SPN is defined for the fencing agent", } loop_control: loop_var: item_to_check when: # - scs_high_availability - (scs_high_availability and scs_cluster_type == "AFA") - not use_msi_for_clusters tags: - always - name: "0.0 Validations - Check required Database HA variables are present and not empty" ansible.builtin.assert: that: - item_to_check.parameter is defined # Has the variable been defined - item_to_check.parameter | type_debug != 'NoneType' # Is the variable not empty" - item_to_check.parameter | trim | length > 1 fail_msg: "{{ item_to_check.error }}" loop: - { parameter: "database_loadbalancer_ip", error: "A highly available SCS deployment requires that scs_lb_ip is provided", } - { parameter: "fencing_spn_client_id", error: "A highly available SCS deployment requires that an SPN is defined for the fencing agent", } - { parameter: "fencing_spn_client_pwd", error: "A highly available SCS deployment requires that an SPN is defined for the fencing agent", } - { parameter: "fencing_spn_subscription_id", error: "A highly available SCS deployment requires that an SPN is defined for the fencing agent", } - { parameter: "fencing_spn_tenant_id", error: "A highly available SCS deployment requires that an SPN is defined for the fencing agent", } loop_control: loop_var: item_to_check when: # - db_high_availability - (database_high_availability and database_cluster_type == "AFA") - platform in ["HANA", "DB2"] tags: - always - name: "0.0 Validations - sharedHome variables" ansible.builtin.debug: var: sid_to_be_deployed when: MULTI_SIDS is defined loop: "{{ MULTI_SIDS }}" loop_control: loop_var: sid_to_be_deployed tags: - always - name: "0.0 Validations - Validate ORACLE parameters (ora_release and ora_version)" when: - platform in ['ORACLE', 'ORACLE-ASM'] ansible.builtin.assert: that: - ora_release is version(ora_supported_version,'>=') - ora_version is version(ora_supported_version,'>=') fail_msg: "Oracle deployments requires that ora_release and ora_version are provided" tags: - always - name: "0.0 Validations - Validate ORACLE parameters (oracle_sbp_patch)" when: - platform in ['ORACLE', 'ORACLE-ASM'] ansible.builtin.assert: that: - oracle_sbp_patch is defined - oracle_sbp_patch | default('') | trim | length > 1 fail_msg: "Oracle deployments requires that oracle_sbp_patch is provided" tags: - always - name: "0.0 Validations - Show ORACLE parameters" when: - platform in ['ORACLE', 'ORACLE-ASM'] ansible.builtin.debug: msg: - "ora_release: {{ ora_release }}" - "ora_version: {{ ora_version }}" - "oracle_sbp_patch: {{ oracle_sbp_patch }}" tags: - always - name: "0.0 Validations - Validate SQL Server parameters" when: - platform == 'SQLSERVER' ansible.builtin.assert: that: - domain_name is defined # Has the variable been defined - domain_name | type_debug != 'NoneType' # Does it have a value - domain_name | trim | length > 1 fail_msg: 'SQL Server deployments require that domain is specified' tags: - always - name: "0.0 Validations - Get Access Token" ansible.builtin.command: >- az account get-access-token --query accessToken -o tsv changed_when: false register: access_token_data no_log: true tags: - always - name: "0.0 Validations - Save the access token" ansible.builtin.set_fact: access_token: "{{ access_token_data.stdout }}" no_log: true tags: - always - name: "0.0 Validations - Retrieve Subscription ID and Resource Group Name" ansible.builtin.uri: url: http://169.254.169.254/metadata/instance?api-version=2021-02-01 use_proxy: false headers: Metadata: true register: azure_metadata tags: - always - name: "0.0 Validations - Show IMDS results (JSON)" ansible.builtin.debug: var: azure_metadata.json verbosity: 2 - name: "0.0 Validations - Extract details" ansible.builtin.set_fact: subscription_id: "{{ azure_metadata.json.compute.subscriptionId }}" resource_group_name: "{{ azure_metadata.json.compute.resourceGroupName }}" mnt_free_diskspace: "{{ ansible_mounts | json_query('[?mount == `/mnt`].size_available') }}" tags: - always - name: "0.0 Validations - Show IMDS results" ansible.builtin.debug: var: mnt_free_diskspace tags: - always - name: "0.0 Validations - Deployer disk space requirements" ansible.builtin.set_fact: deployer_free_temp_disk_space: 40 when: - deployer_free_temp_disk_space is not defined tags: - always - name: "0.0 Validations - Check for free disk space on deployer" ansible.builtin.assert: that: (mnt_free_diskspace | first | int / (1024 * 1024 * 1024) | int) > ( deployer_free_temp_disk_space ) fail_msg: "The deployer needs at least {{ deployer_free_temp_disk_space }} GB of free disk space in /mnt" when: - mnt_free_diskspace | length > 0 tags: - 0.0-agent-diskspace - name: "0.0 Validations - Check SAP CAL variables are present and not empty" when: enable_sap_cal is defined and enable_sap_cal ansible.builtin.assert: that: - calapi_kv is defined - calapi_kv | type_debug != 'NoneType' - calapi_kv | trim | length > 1 fail_msg: "Please provide the SAP CAL API key vault name in calapi_kv parameter" tags: - always # /*---------------------------------------------------------------------------8 # | | # | Validate the server side parameters | # | | # +------------------------------------4--------------------------------------*/ - hosts: "{{ sap_sid | upper }}_DB : {{ sap_sid | upper }}_SCS : {{ sap_sid | upper }}_ERS : {{ sap_sid | upper }}_PAS : {{ sap_sid | upper }}_APP : {{ sap_sid | upper }}_WEB : {{ sap_sid | upper }}_OBSERVER_DB" name: "0.0 Validations - Validate SAP-Parameters" gather_facts: false vars_files: - vars/ansible-input-api.yaml # API Input template with defaults tasks: - name: "0.0 Validations: - Wait for system to become reachable" ansible.builtin.wait_for_connection: timeout: 120 register: wait_for_connection_results tags: - always - name: "0.0 Validations: - Gather facts for first time" ansible.builtin.setup: tags: - always - name: "0.0 Validations: - Set distro" ansible.builtin.set_fact: distro_family: "{{ ansible_os_family | upper }}" distribution_id: "{{ ansible_distribution | lower ~ ansible_distribution_major_version }}" distribution_full_id: "{{ ansible_distribution | lower ~ ansible_distribution_version }}" tags: - always - name: "0.0 Validations: - Show distro" ansible.builtin.debug: msg: - "Distro family: {{ distro_family }}" - "Distribution id: {{ distribution_id }}" - "Distribution full id: {{ distribution_full_id }}" tags: - always - name: "0.0 Validations: - Set Python version {{ distribution_id }}" when: (ansible_distribution | lower ~ ansible_distribution_major_version) in ['sles_sap12'] ansible.builtin.set_fact: python_version: "python2" tags: - always - name: "0.0 Validations - Get details from local host" ansible.builtin.set_fact: subscription_id: "{{ hostvars.localhost.subscription_id }}" resource_group_name: "{{ hostvars.localhost.resource_group_name }}" access_token: "{{ hostvars.localhost.access_token }}" no_log: true tags: - always - name: "0.0 Validations - Variables" ansible.builtin.debug: var: hostvars verbosity: 4 - name: "0.0 Validations - groupsnames" ansible.builtin.debug: var: group_names verbosity: 4 - name: "0.0 Validations - Variables" ansible.builtin.debug: msg: - "Virtual Host Name: {{ virtual_host }}" - "DNS: {{ ansible_domain }}" - name: "0.0 Validations - Validate SCS and HDB instance numbers" ansible.builtin.assert: that: - "scs_instance_number != db_instance_number" fail_msg: "Please ensure that the scs_instance_number is different from the db_instance_number when performing a standalone installation" when: - single_server - platform == "HANA" tags: - always - 0.0-scs-db-instance-single - name: "0.0 Validations - Validate SCS and HDB SIDs" when: - single_server - platform != "ORACLE" ansible.builtin.assert: that: - "sap_sid != db_sid" fail_msg: "Please ensure that the sap_sid is different from the db_sid when performing a standalone installation" tags: - always - name: Validate SCS and PAS instance numbers ansible.builtin.assert: that: - scs_instance_number != pas_instance_number fail_msg: "Please ensure that the pas_instance_number is different from the scs_instance_number when installing PAS on ASCS" when: (ansible_play_hosts_all | length) == 2 tags: - always - 0.0-scs-pas - name: "0.0 Validations - Validate SCS and PAS instance numbers" ansible.builtin.assert: that: - scs_instance_number != pas_instance_number fail_msg: "Please ensure that the pas_instance_number is different from the scs_instance_number on standalone installation" when: single_server tags: - always - 0.0-scs-pas-single - name: "0.0 Validations - Validate DB and PAS instance numbers" ansible.builtin.assert: that: - db_instance_number != pas_instance_number fail_msg: "Please ensure that the pas_instance_number is different from the db_instance_number on standalone installation" when: single_server tags: - always - 0.0-scs-pas-db-single # url: "https://management.azure.com/subscriptions/{{ subscription_id }}/providers/Microsoft.Web/sites?api-version=2022-03-01" # url: "https://azure.status.microsoft/en-us/status" - name: "0.0 Validations - Check internet connectivity" ansible.builtin.uri: url: "{{ url_internet }}" # headers: # Authorization: "Bearer {{ access_token }}" # Content-Type: application/json # Accept: application/json status_code: - 200 - 403 register: internet_check vars: ansible_python_interpreter: "{{ python_version }}" when: - (ansible_distribution | lower ~ ansible_distribution_major_version) in ['suse15', 'redhat8', 'redhat9', 'sles_sap15' ] - ansible_os_family != "Windows" - check_outbound | bool tags: - always - 0.0-internet - name: "0.0 Validations - Check internet connectivity" ansible.builtin.uri: url: "https://management.azure.com/subscriptions/{{ subscription_id }}/providers/Microsoft.Web/sites?api-version=2022-03-01" headers: Authorization: "Bearer {{ access_token }}" Content-Type: application/json Accept: application/json status_code: - 200 - 403 when: - (ansible_distribution | lower ~ ansible_distribution_major_version) in ['suse15', 'redhat8', 'redhat9', 'sles_sap15' ] - ansible_os_family != "Windows" - check_outbound | bool tags: - always - 0.0-internet - name: "0.0 Validations - Show Mounts" ansible.builtin.debug: msg: - "Mount: {{ item.mount }}" - "Device: {{ item.device }}" - "Filesystem: {{ item.fstype }}" with_items: - "{{ ansible_mounts }}" when: - ansible_os_family != "Windows" - name: "0.0 Validations - Create directories" become: true ansible.builtin.file: path: '/etc/sap_deployment_automation/{{ sap_sid | upper }}' state: directory mode: '0755' when: - ansible_os_family != "Windows" tags: - always - name: "0.0 Validations - Show Hosts" when: - ansible_os_family != "Windows" ansible.builtin.shell: set -o pipefail && cat /etc/hosts | grep -v -e "^#" | grep -v -e '^[[:space:]]*$' register: hosts tags: - always - 0.0-hosts - name: "0.0 Validations - Show Hosts" ansible.builtin.debug: var: hosts.stdout_lines when: - hosts is defined - ansible_os_family != "Windows" - name: "0.0 Validations - Create validation-done flag" delegate_to: localhost become: false ansible.builtin.file: path: "{{ _workspace_directory }}/.progress/validation-done" state: touch mode: 0755 tags: - always - name: "0.0 Validations - Netmask" when: - ansible_os_family != "Windows" ansible.builtin.debug: msg: - "Netmask: {{ hostvars[ansible_hostname]['ansible_default_ipv4']['netmask'] }}" - "CIDR: {{ ((hostvars[ansible_hostname]['ansible_default_ipv4']['address'] | string) + '/' + (hostvars[ansible_hostname]['ansible_default_ipv4']['netmask'] | string)) | ansible.utils.ipaddr('prefix') }}" - "IPV4: {{ hostvars[ansible_hostname]['ansible_default_ipv4']['address'] }}" - name: Check if /usr/sap is already mounted when: - ansible_os_family != "Windows" block: - name: Check if /usr/sap is already mounted ansible.builtin.set_fact: usr_sap_mounted: true with_items: "{{ ansible_mounts }}" when: - item.mount == "/usr/sap" - "'scs' in supported_tiers" tags: - always - name: "0.0 Validations - Disk space details" when: - "'scs' in supported_tiers" - usr_sap_mounted is defined - ansible_os_family != "Windows" block: - name: "0.0 Validations - Disk space details" ansible.builtin.set_fact: free_diskspace: "{{ ansible_mounts | json_query('[?mount == `/usr/sap`].size_available') }}" tags: - always - name: "0.0 Validations - SCS /usr/sap disk space requirements" ansible.builtin.set_fact: scs_free_diskspace: 10 when: - scs_free_diskspace is not defined tags: - always - name: "0.0 Validations - Show SCS disk space" ansible.builtin.debug: msg: - "Free disk space: {{ free_diskspace | first | int / (1024 * 1024 * 1024) | int }}" verbosity: 2 - name: "0.0 Validations - Show SCS disk space required" ansible.builtin.debug: msg: - "Free disk space required: {{ scs_free_diskspace }}" verbosity: 2 - name: "0.0 Validations - Check for free disk space on SCS" ansible.builtin.assert: that: - (free_diskspace | first | int / (1024 * 1024 * 1024) | int) > (scs_free_diskspace | int) fail_msg: "The SCS server needs at least {{ scs_free_diskspace }} GB of free disk space in /mnt" tags: - 0.0-scs-diskspace tags: - always # /*---------------------------------------------------------------------------8 # | | # | Validate the server side parameters - Oracle | # | | # +------------------------------------4--------------------------------------*/ - hosts: "{{ sap_sid | upper }}_DB : {{ sap_sid | upper }}_PAS : {{ sap_sid | upper }}_APP" name: "0.0 Validations - Validate SAP-Parameters for Oracle" gather_facts: true vars_files: - vars/ansible-input-api.yaml # API Input template with defaults tasks: - name: "0.0 Validations - Validate that the correct distribution is used" ansible.builtin.assert: that: - "distribution_id == 'oraclelinux8'" fail_msg: "For Oracle deployments DB, PAS and APP needs to run on OracleLinux" when: - platform in ['ORACLE', 'ORACLE-ASM'] - hosts: "{{ sap_sid | upper }}_DB" name: "0.0 Validations - Validate SAP-Parameters for Oracle" gather_facts: true vars_files: - vars/ansible-input-api.yaml # API Input template with defaults tasks: - name: "0.0 Validations - Validate that MULTISID is provided" ansible.builtin.assert: that: - MULTI_SIDS is defined # Has the variable been defined - MULTI_SIDS | length > 0 fail_msg: "Please specify MULTI_SIDS if you are deploying a shared home installation" when: node_tier == 'oracle-multi-sid' - hosts: "{{ sap_sid | upper }}_WEB" name: "0.0 Validations - Validate SAP-Parameters for Web Dispatchers" gather_facts: true vars_files: - vars/ansible-input-api.yaml # API Input template with defaults tasks: - name: "0.0 Validations - VAlidate web_sid presence of Web Dispatchers are deployed" ansible.builtin.assert: that: - web_sid is defined # Has the variable been defined - web_sid | type_debug != 'NoneType' # and given a value - web_sid | trim | length > 1 fail_msg: "Please specify web_sid if you are deploying web dispatchers" when: (ansible_play_hosts_all | length) > 0 # /*---------------------------------------------------------------------------8 # | END | # +------------------------------------4--------------------------------------*/