diff --git a/roles/sap_ha_pacemaker_cluster/defaults/main.yml b/roles/sap_ha_pacemaker_cluster/defaults/main.yml index b50453b94..785406f79 100644 --- a/roles/sap_ha_pacemaker_cluster/defaults/main.yml +++ b/roles/sap_ha_pacemaker_cluster/defaults/main.yml @@ -45,7 +45,7 @@ sap_ha_pacemaker_cluster_operation_defaults: {} # nwas_abap_ascs_ers (available) # nwas_abap_pas_aas (not yet) # nwas_java_scs_ers (available) - +# sap_webdisp (beta) # 'sap_ha_pacemaker_cluster_host_type' is converted from string to list type in # 'tasks/ascertain_sap_landscape.yml'. sap_ha_pacemaker_cluster_host_type: "{{ sap_host_type | d(['hana_scaleup_perf']) }}" @@ -312,6 +312,54 @@ sap_ha_pacemaker_cluster_nwas_ers_sapinstance_automatic_recover_bool: false # sap_ha_pacemaker_cluster_nwas_abap_aas_filesystem_resource_name: '' # sap_ha_pacemaker_cluster_nwas_abap_aas_sapinstance_resource_name: '' +################################################################################ +# Web Dispatcher resource defaults +################################################################################ + +#TODO - check if these need to be moved to include_vars_webdisp.yml and converted to __* + +sap_ha_pacemaker_cluster_wdp_sid: "{{ sap_swpm_wdp_sid | d('') }}" +sap_ha_pacemaker_cluster_wdp_instance_nr: "{{ sap_swpm_wdp_instance_nr | d('') }}" + +sap_ha_pacemaker_cluster_vip_wdp_ip_address: '' +sap_ha_pacemaker_cluster_vip_wdp_resource_name: >- + rsc_vip_{{ sap_ha_pacemaker_cluster_wdp_sid }}_WDP{{ sap_ha_pacemaker_cluster_wdp_instance_nr }} +sap_ha_pacemaker_cluster_healthcheck_wdp_resource_name: >- + rsc_vip_health_check_{{ sap_ha_pacemaker_cluster_wdp_sid }}_WDP{{ sap_ha_pacemaker_cluster_wdp_instance_nr }} + +# Name of the instance profile - mandatory to be user-defined +sap_ha_pacemaker_cluster_wdp_sapinstance_instance_name: '' +# Full path with instance profile name - mandatory to be user-defined +sap_ha_pacemaker_cluster_wdp_sapinstance_start_profile_string: '' + +sap_ha_pacemaker_cluster_wdp_filesystem_resource_name: >- + rsc_fs_{{ sap_ha_pacemaker_cluster_wdp_sid }}_WDP{{ sap_ha_pacemaker_cluster_wdp_instance_nr }} +sap_ha_pacemaker_cluster_wdp_sapinstance_resource_name: >- + rsc_SAPInstance_{{ sap_ha_pacemaker_cluster_wdp_sid }}_WDP{{ sap_ha_pacemaker_cluster_wdp_instance_nr }} +sap_ha_pacemaker_cluster_wdp_sapstartsrv_resource_name: >- + rsc_SAPStartSrv_{{ sap_ha_pacemaker_cluster_wdp_sid }}_WDP{{ sap_ha_pacemaker_cluster_wdp_instance_nr }} + +sap_ha_pacemaker_cluster_vip_wdp_resource_group_name: >- + grp_{{ sap_ha_pacemaker_cluster_wdp_sid }}_WDP{{ sap_ha_pacemaker_cluster_wdp_instance_nr }} + +# Currently unused +# sap_ha_pacemaker_cluster_wdp_order_wdp_last_name: >- +# ord_wdp_last_{{ sap_ha_pacemaker_cluster_wdp_sid }} + +sap_ha_pacemaker_cluster_wdp_sapinstance_automatic_recover_bool: false +sap_ha_pacemaker_cluster_wdp_sapinstance_resource_stickiness: 5000 + +# Stickiness of the WebDisp group +sap_ha_pacemaker_cluster_wdp_group_stickiness: 3000 + +# Clustered filesystem for Web Dispatcher +sap_ha_pacemaker_cluster_wdp_filesystem_host_mount_path: '' +sap_ha_pacemaker_cluster_wdp_filesystem_local_mount_path: "/usr/sap/{{ sap_ha_pacemaker_cluster_wdp_sid }}/W{{ sap_ha_pacemaker_cluster_wdp_instance_nr }}" +sap_ha_pacemaker_cluster_wdp_filesystem_fstype: "{{ sap_ha_pacemaker_cluster_storage_nfs_filesytem_type }}" +sap_ha_pacemaker_cluster_wdp_filesystem_options_string: "{{ sap_ha_pacemaker_cluster_storage_nfs_mount_options }}" +sap_ha_pacemaker_cluster_wdp_filesystem_force_unmount: "{{ sap_ha_pacemaker_cluster_resource_filesystem_force_unmount }}" + +sap_ha_pacemaker_cluster_healthcheck_wdp_id: "{{ sap_ha_pacemaker_cluster_wdp_sid + 'wdp' }}" ################################################################################ # Platform specific diff --git a/roles/sap_ha_pacemaker_cluster/meta/argument_specs.yml b/roles/sap_ha_pacemaker_cluster/meta/argument_specs.yml index 8f699c35d..eb0b312c5 100644 --- a/roles/sap_ha_pacemaker_cluster/meta/argument_specs.yml +++ b/roles/sap_ha_pacemaker_cluster/meta/argument_specs.yml @@ -136,6 +136,7 @@ argument_specs: - nwas_abap_ascs_ers # - nwas_abap_pas_aas - nwas_java_scs_ers + - sap_webdisp default: hana_scaleup_perf description: - The SAP landscape to for which the cluster is to be configured. @@ -968,6 +969,85 @@ argument_specs: # TODO: implement AAS cluster setup + ######################################################################### + # WebDisp specific parameters + ########################################################################## + + sap_ha_pacemaker_cluster_wdp_sid: + description: + - System ID (SID) of the Web Dispatcher in Capital letters. + - Defaults to `sap_swpm_wdp_sid` if defined. + - Mandatory for Web Dispatcher cluster scenarios. + + sap_ha_pacemaker_cluster_wdp_instance_nr: + description: + - Instance number of the Web Dispatcher instance. + - Defaults to `sap_swpm_wdp_instance_nr` if defined. + - Mandatory for Web Dispatcher cluster configuration. + + sap_ha_pacemaker_cluster_vip_wdp_ip_address: + description: + - Virtual IP of the Web Dispatcher instance. + - Mandatory for the Web Dispatcher cluster setup. + + sap_ha_pacemaker_cluster_vip_wdp_resource_name: + default: rsc_vip__WDP + description: + - Name of the Virtual IP resource for Web Dispatcher. + + sap_ha_pacemaker_cluster_healthcheck_wdp_resource_name: + default: rsc_vip_health_check__WDP + description: + - Name of the Virtual IP Health Check resource for Web Dispatcher. + + sap_ha_pacemaker_cluster_wdp_sapinstance_instance_name: + description: + - The name of the WebDisp instance, typically the profile name. + - Mandatory for the Web Dispatcher cluster setup. + - Recommended format _W_ + + sap_ha_pacemaker_cluster_wdp_sapinstance_start_profile_string: + description: + - The full path and name of the WebDisp instance profile. + - Mandatory for the Web Dispatcher cluster setup. + + sap_ha_pacemaker_cluster_wdp_filesystem_resource_name: + default: rsc_fs__WDP + description: + - Name of the filesystem resource for the WebDisp instance. + + sap_ha_pacemaker_cluster_wdp_sapinstance_resource_name: + default: rsc_SAPInstance__WDP + description: + - Name of the WebDisp instance resource. + + sap_ha_pacemaker_cluster_wdp_sapstartsrv_resource_name: + default: rsc_SAPStartSrv__WDP + description: + - Name of the WebDisp SAPStartSrv resource for simple mount. + + sap_ha_pacemaker_cluster_vip_wdp_resource_group_name: + default: grp__WDP + description: + - Name of the NetWeaver WebDisp resource group. + + sap_ha_pacemaker_cluster_wdp_sapinstance_automatic_recover_bool: + type: bool + default: false + description: + - Web Dispatcher instance resource option "AUTOMATIC_RECOVER". + + sap_ha_pacemaker_cluster_wdp_sapinstance_resource_stickiness: + default: 5000 + description: + - Web Dispatcher instance resource stickiness attribute. + + sap_ha_pacemaker_cluster_wdp_group_stickiness: + default: 3000 + description: + - Web Dispatcher resource group stickiness. + - Defines how sticky is Web Dispatcher group to the node it was started on. + ########################################################################## # Platforms: AWS specific parameters diff --git a/roles/sap_ha_pacemaker_cluster/tasks/ascertain_sap_landscape.yml b/roles/sap_ha_pacemaker_cluster/tasks/ascertain_sap_landscape.yml index c5aa9e643..e42bc5ba1 100644 --- a/roles/sap_ha_pacemaker_cluster/tasks/ascertain_sap_landscape.yml +++ b/roles/sap_ha_pacemaker_cluster/tasks/ascertain_sap_landscape.yml @@ -60,3 +60,9 @@ tags: nwas_postinst when: - sap_ha_pacemaker_cluster_host_type | select('search', 'nwas') | length > 0 + +- name: "SAP HA Prepare Pacemaker - Include Web Dispatcher specific variables" + ansible.builtin.include_tasks: + file: include_vars_webdisp.yml + when: + - sap_ha_pacemaker_cluster_host_type | select('search', 'webdisp') | length > 0 diff --git a/roles/sap_ha_pacemaker_cluster/tasks/configure_webdisp_postinstallation.yml b/roles/sap_ha_pacemaker_cluster/tasks/configure_webdisp_postinstallation.yml new file mode 100644 index 000000000..dbf3ce232 --- /dev/null +++ b/roles/sap_ha_pacemaker_cluster/tasks/configure_webdisp_postinstallation.yml @@ -0,0 +1,242 @@ +# SPDX-License-Identifier: Apache-2.0 +--- +# After SAP Webdispatcher instance was configured in the cluster, +# they must be disabled from automatically (re)starting outside of +# cluster control. + +- name: "SAP HA Pacemaker - (WebDisp profile) Prevent automatic restart of the instance" + ansible.builtin.replace: + path: "{{ sap_ha_pacemaker_cluster_wdp_sapinstance_start_profile_string }}" + backup: true + regexp: 'Restart_Program_01' + replace: 'Start_Program_01' + # Throttle and retry loop was added to combat NFS write lockups on Azure NFS + throttle: 1 + retries: 30 + delay: 10 + +# Comment out lines in /usr/sap/sapservices, which +# - contain the target instance profile names +# - are not commented out yet +- name: "SAP HA Pacemaker - Update /usr/sap/sapservices" + ansible.builtin.replace: + path: /usr/sap/sapservices + backup: true + regexp: '^([^#\n].+{{ sap_ha_pacemaker_cluster_wdp_sapinstance_instance_name }}.+)$' + replace: '# \1' + +- name: "SAP HA Pacemaker - (systemd) Check for WebDisp services" + ansible.builtin.stat: + path: "/etc/systemd/system/SAP{{ sap_ha_pacemaker_cluster_wdp_sid }}_{{ sap_ha_pacemaker_cluster_wdp_instance_nr }}.service" + register: __sap_ha_pacemaker_cluster_register_instance_service + +- name: "SAP HA Pacemaker - (systemd) Save found WebDisp services" + ansible.builtin.set_fact: + sap_ha_pacemaker_cluster_instance_services_fact: "{{ __sap_ha_pacemaker_cluster_register_instance_service.stat.path | regex_replace('/etc/systemd/system/', '') }}" + when: __sap_ha_pacemaker_cluster_register_instance_service.stat.exists + + +# BLOCK: +# When the systemd based SAP startup framework is used, make sure that the +# instance services do not auto-start. +- name: "SAP HA Pacemaker - Block to disable systemd auto-start of instances" + when: + - sap_ha_pacemaker_cluster_instance_services_fact is defined + - sap_ha_pacemaker_cluster_instance_services_fact | length > 0 + block: + + - name: "SAP HA Pacemaker - (systemd) Disable WebDisp instance service" + ansible.builtin.service: + name: "{{ sap_ha_pacemaker_cluster_instance_services_fact }}" + enabled: false + + # Creates a config file for the services. + # Parent directories will be created when missing. + - name: "SAP HA Pacemaker - (systemd) Create WebDisp instance unit config file" + ansible.builtin.lineinfile: + create: true + path: "/etc/systemd/system/{{ sap_ha_pacemaker_cluster_instance_services_fact }}.d/HA.conf" + line: "[Service]" + owner: root + group: root + mode: '0644' + + - name: "SAP HA Pacemaker - (systemd) Disable WebDisp instance unit auto-restart" + ansible.builtin.lineinfile: + path: "/etc/systemd/system/{{ sap_ha_pacemaker_cluster_instance_services_fact }}.d/HA.conf" + regex: '^Restart\s*=\s*no' + insertafter: '^[Service]$' + line: "Restart=no" + owner: root + group: root + mode: '0644' +### END of BLOCK for systemd setup. + + +# Block for configuring the SAP HA Interface (sap_cluster_connector). +# +# The 'sap-cluster-connector' package is already optionally added to +# '__sap_ha_pacemaker_cluster_sap_extra_packages'. +- name: "SAP HA Pacemaker - (SAP HA Interface) Configure SAP HA Interface" + when: + - sap_ha_pacemaker_cluster_enable_cluster_connector + block: + + - name: "SAP HA Pacemaker - (SAP HA Interface) Add {{ sap_ha_pacemaker_cluster_wdp_sid | lower }}adm + user to 'haclient' group" # noqa name[template] + ansible.builtin.user: + name: "{{ sap_ha_pacemaker_cluster_wdp_sid | lower }}adm" + groups: haclient + append: true + state: present + + # Using 'lineinfile' with a nested loop to avoid duplicate entries for existing configuration. + - name: "SAP HA Pacemaker - (SAP HA Interface) Add connector to start profiles" + ansible.builtin.lineinfile: + backup: true + path: "{{ nwas_profile_item.0 }}" + line: "{{ nwas_profile_item.1 }}" + loop: "{{ __sap_ha_pacemaker_cluster_wdp_profile_paths + | product(__sap_ha_pacemaker_cluster_connector_config_lines) + }}" + loop_control: + loop_var: nwas_profile_item + label: "{{ nwas_profile_item.0 }} -> {{ nwas_profile_item.1 }}" + # Throttle and retry loop was added to combat NFS write lockups on Azure NFS + throttle: 1 + retries: 30 + delay: 10 + + # Sleep added to resolve issue with WaitforStarted finishing before resources are available. + - name: "SAP HA Pacemaker - (SAP HA Interface) Wait for WebDisp to be up and running" + become: true + become_user: "{{ sap_ha_pacemaker_cluster_wdp_sid | lower }}adm" + register: __sap_ha_pacemaker_cluster_register_where_wdp + ansible.builtin.shell: | + /usr/sap/hostctrl/exe/sapcontrol -nr {{ sap_ha_pacemaker_cluster_wdp_instance_nr }} -function WaitforStarted 600 30 + changed_when: false + failed_when: false + + # NOTE: RestartService can cause fencing lockup and hang forever, + # it might be good to remove them in future and leave reload to "Webdisp restart" block. + - name: "SAP HA Pacemaker - (SAP HA Interface) Restart the service" + when: + - __sap_ha_pacemaker_cluster_register_where_wdp.rc == 0 + become: true + become_user: "{{ sap_ha_pacemaker_cluster_wdp_sid | lower }}adm" + register: __sap_ha_pacemaker_cluster_register_restart_ascs + ansible.builtin.shell: | + /usr/sap/hostctrl/exe/sapcontrol -nr {{ sap_ha_pacemaker_cluster_wdp_instance_nr }} -function RestartService + changed_when: __sap_ha_pacemaker_cluster_register_restart_ascs.rc == 0 + + - name: "SAP HA Pacemaker - (SAP HA Interface) Get HAGetFailoverConfig for WebDisp" + when: + - __sap_ha_pacemaker_cluster_register_where_wdp.rc == 0 + become: true + become_user: "{{ sap_ha_pacemaker_cluster_wdp_sid | lower }}adm" + register: __sap_ha_pacemaker_cluster_register_wdp_ha_failover_config + ansible.builtin.shell: | + sleep 10 + /usr/sap/hostctrl/exe/sapcontrol -nr {{ sap_ha_pacemaker_cluster_wdp_instance_nr }} -function HAGetFailoverConfig + changed_when: false + + - name: "SAP HA Pacemaker - (SAP HA Interface) Display HAGetFailoverConfig results" + when: + - __sap_ha_pacemaker_cluster_register_where_wdp.rc == 0 + - __sap_ha_pacemaker_cluster_register_wdp_ha_failover_config.stdout_lines is defined + ansible.builtin.debug: + msg: | + {{ __sap_ha_pacemaker_cluster_register_wdp_ha_failover_config.stdout_lines }} + + + - name: "SAP HA Pacemaker - (SAP HA Interface) Get HACheckConfig for WebDisp" + when: + - __sap_ha_pacemaker_cluster_register_where_wdp.rc == 0 + become: true + become_user: "{{ sap_ha_pacemaker_cluster_wdp_sid | lower }}adm" + register: __sap_ha_pacemaker_cluster_register_wdp_ha_check_config + ansible.builtin.shell: | + /usr/sap/hostctrl/exe/sapcontrol -nr {{ sap_ha_pacemaker_cluster_wdp_instance_nr }} -function HACheckConfig + changed_when: false + failed_when: false + + - name: "SAP HA Pacemaker - (SAP HA Interface) Display HACheckConfig results" + when: + - __sap_ha_pacemaker_cluster_register_where_wdp.rc == 0 + - __sap_ha_pacemaker_cluster_register_wdp_ha_check_config.stdout_lines is defined + ansible.builtin.debug: + msg: | + {{ __sap_ha_pacemaker_cluster_register_wdp_ha_check_config.stdout_lines }} + + + # Block to restart cluster resources if RestartService is not enough. + # This is required for SUSE, where SAP needs full restart to load HAlib. + - name: "SAP HA Pacemaker - (SAP HA Interface) Block for WebDisp restart" + when: + - "(__sap_ha_pacemaker_cluster_register_wdp_ha_failover_config.stdout is defined + and 'FALSE' in __sap_ha_pacemaker_cluster_register_wdp_ha_failover_config.stdout)" + block: + - name: "SAP HA Pacemaker - (SAP HA Interface) Restart WebDisp resources" + ansible.builtin.shell: | + {{ __sap_ha_pacemaker_cluster_command.resource_restart }} {{ sap_ha_pacemaker_cluster_wdp_sapinstance_resource_name }} + when: + - __sap_ha_pacemaker_cluster_register_where_wdp.rc == 0 + changed_when: true + + - name: "SAP HA Pacemaker - (SAP HA Interface) Wait for WebDisp to be up and running" + become: true + become_user: "{{ sap_ha_pacemaker_cluster_wdp_sid | lower }}adm" + register: __sap_ha_pacemaker_cluster_register_where_wdp_restart + ansible.builtin.shell: | + /usr/sap/hostctrl/exe/sapcontrol -nr {{ sap_ha_pacemaker_cluster_wdp_instance_nr }} -function WaitforStarted 600 30 + changed_when: false + failed_when: false + + - name: "SAP HA Pacemaker - (SAP HA Interface) Get HACheckConfig for WebDisp" + when: + - __sap_ha_pacemaker_cluster_register_where_wdp.rc == 0 + become: true + become_user: "{{ sap_ha_pacemaker_cluster_wdp_sid | lower }}adm" + register: __sap_ha_pacemaker_cluster_register_wdp_ha_check_config + ansible.builtin.shell: | + sleep 30 + /usr/sap/hostctrl/exe/sapcontrol -nr {{ sap_ha_pacemaker_cluster_wdp_instance_nr }} -function HACheckConfig + changed_when: false + failed_when: + - "'ERROR' in __sap_ha_pacemaker_cluster_register_wdp_ha_check_config.stdout" + + - name: "SAP HA Pacemaker - (SAP HA Interface) Get HAGetFailoverConfig for WebDisp" + when: + - __sap_ha_pacemaker_cluster_register_where_wdp.rc == 0 + become: true + become_user: "{{ sap_ha_pacemaker_cluster_wdp_sid | lower }}adm" + register: __sap_ha_pacemaker_cluster_register_wdp_ha_failover_config + ansible.builtin.shell: | + /usr/sap/hostctrl/exe/sapcontrol -nr {{ sap_ha_pacemaker_cluster_wdp_instance_nr }} -function HAGetFailoverConfig + changed_when: false + # failed_when: + # - __sap_ha_pacemaker_cluster_register_wdp_ha_failover_config.stdout is defined + # and 'FALSE' in __sap_ha_pacemaker_cluster_register_wdp_ha_failover_config.stdout + + + # HAGetFailoverConfig is not consistent and it can show FALSE on one of nodes + - name: "SAP HA Pacemaker - (SAP HA Interface) Display HAGetFailoverConfig results on WebDisp" + when: + - __sap_ha_pacemaker_cluster_register_where_wdp.rc == 0 + - __sap_ha_pacemaker_cluster_register_wdp_ha_failover_config.stdout_lines is defined + ansible.builtin.debug: + msg: | + {{ __sap_ha_pacemaker_cluster_register_wdp_ha_failover_config.stdout_lines }} + + # HACheckConfig shows same statues on both nodes, therefore only one is shown + - name: "SAP HA Pacemaker - (SAP HA Interface) Display HACheckConfig results" + when: + - __sap_ha_pacemaker_cluster_register_where_wdp.rc == 0 + - __sap_ha_pacemaker_cluster_register_wdp_ha_check_config.stdout_lines is defined + ansible.builtin.debug: + msg: | + {{ __sap_ha_pacemaker_cluster_register_wdp_ha_check_config.stdout_lines }} + + # TODO: verification checks that the instances are running and HA Interface is enabled + +### END of BLOCK for sap_cluster_connector. diff --git a/roles/sap_ha_pacemaker_cluster/tasks/construct_vars_common2.yml b/roles/sap_ha_pacemaker_cluster/tasks/construct_vars_common2.yml new file mode 100644 index 000000000..9edf3f7ba --- /dev/null +++ b/roles/sap_ha_pacemaker_cluster/tasks/construct_vars_common2.yml @@ -0,0 +1,125 @@ +# SPDX-License-Identifier: Apache-2.0 +--- +# TEMP to fix CFLF +# Combine input parameters with inherited vars from the 'ha_cluster' role. +# The inherited values take precedence. Some parameters are not required to be set. +# The 'ha_cluster' LSR will apply its role defaults. +# For mandatory parameters, sanity checks will be done separately. + +# sap_ha_pacemaker_cluster_cluster_name -> user-defined or default inherited from {{ ha_cluster_cluster_name }} +- name: "SAP HA Prepare Pacemaker - Set cluster name" + when: + - __sap_ha_pacemaker_cluster_cluster_name is not defined + - sap_ha_pacemaker_cluster_cluster_name is defined + ansible.builtin.set_fact: + __sap_ha_pacemaker_cluster_cluster_name: "{{ sap_ha_pacemaker_cluster_cluster_name }}" + +# sap_ha_pacemaker_cluster_hacluster_user_password -> user-defined or default inherited from {{ ha_cluster_hacluster_password }} +- name: "SAP HA Prepare Pacemaker - Register the 'hacluster' user password" + when: + - __sap_ha_pacemaker_cluster_hacluster_user_password is not defined + - sap_ha_pacemaker_cluster_hacluster_user_password + ansible.builtin.set_fact: + __sap_ha_pacemaker_cluster_hacluster_user_password: "{{ sap_ha_pacemaker_cluster_hacluster_user_password }}" + no_log: true # secure the credential + +# sap_ha_pacemaker_cluster_ha_cluster -> user-defined or default inherited from {{ ha_cluster }} +- name: "SAP HA Prepare Pacemaker - Register sap_ha_pacemaker_cluster_ha_cluster" + when: + - __sap_ha_pacemaker_cluster_ha_cluster is not defined + - sap_ha_pacemaker_cluster_ha_cluster is defined + ansible.builtin.set_fact: + __sap_ha_pacemaker_cluster_ha_cluster: "{{ sap_ha_pacemaker_cluster_ha_cluster }}" + +- name: "SAP HA Prepare Pacemaker - Generate default sap_ha_pacemaker_cluster_ha_cluster" + when: + - not __sap_ha_pacemaker_cluster_ha_cluster is defined + - not sap_ha_pacemaker_cluster_ha_cluster is defined + ansible.builtin.set_fact: + __sap_ha_pacemaker_cluster_ha_cluster: + node_name: "{{ ansible_hostname }}" + pcs_address: "{{ ansible_default_ipv4.address }}" + + +# Combine following extra packages together: +# sap_ha_pacemaker_cluster_extra_packages -> user-defined, empty by global default +# __sap_ha_pacemaker_cluster_sap_extra_packages -> included from vars/* +# __sap_ha_pacemaker_cluster_platform_extra_packages -> included from vars/platform* + +- name: "SAP HA Prepare Pacemaker - Combine extra packages lists" + ansible.builtin.set_fact: + __sap_ha_pacemaker_cluster_extra_packages: "{{ + (sap_ha_pacemaker_cluster_extra_packages + + __sap_ha_pacemaker_cluster_sap_extra_packages + + __sap_ha_pacemaker_cluster_platform_extra_packages) + | unique | select() }}" + # remove duplicates and empty elements + + +# Combine following fence packages together: +# __sap_ha_pacemaker_cluster_fence_agent_packages_minimal -> os default +# __sap_ha_pacemaker_cluster_fence_agent_packages_platform -> platform defaults from dict +# sap_ha_pacemaker_cluster_fence_agent_packages -> user input or default [] + +# __sap_ha_pacemaker_cluster_fence_agent_packages loaded from ha_cluster is not included, +# because it would still not be used due to precedence. +# TODO: Remove Tech debt conditionals in future for deprecated var 'sap_ha_pacemaker_cluster_fence_agent_minimal_packages' +- name: "SAP HA Prepare Pacemaker - Combine fence agent packages lists" + ansible.builtin.set_fact: + __sap_ha_pacemaker_cluster_fence_agent_packages: "{{ + (__sap_ha_pacemaker_cluster_fence_agent_packages_minimal_combined + + __sap_ha_pacemaker_cluster_fence_agent_packages_platform + + sap_ha_pacemaker_cluster_fence_agent_packages) + | unique }}" + vars: + # Tech debt for sap_ha_pacemaker_cluster_fence_agent_minimal_packages + __sap_ha_pacemaker_cluster_fence_agent_packages_minimal_combined: + "{{ __sap_ha_pacemaker_cluster_fence_agent_packages_minimal + + sap_ha_pacemaker_cluster_fence_agent_minimal_packages + if (sap_ha_pacemaker_cluster_fence_agent_minimal_packages is defined + and sap_ha_pacemaker_cluster_fence_agent_minimal_packages | length > 0 + and sap_ha_pacemaker_cluster_fence_agent_minimal_packages is iterable) + else __sap_ha_pacemaker_cluster_fence_agent_packages_minimal }}" + + +# Prepare corosync totem variable with either: +# - User provided sap_ha_pacemaker_cluster_corosync_totem if present +# - Combine corosync totem from OS variables and Platform variables if present +# - Use default corosync totem from OS variables if Platform variable is not present +- name: "SAP HA Prepare Pacemaker - Prepare corosync totem settings" + ansible.builtin.set_fact: + __sap_ha_pacemaker_cluster_corosync_totem: + options: "{{ __sap_ha_pacemaker_cluster_corosync_totem.options | d([]) + __totem_settings }}" + vars: + # Identify if provided sap_ha_pacemaker_cluster_corosync_totem is defined + __user_totem_is_present: + "{{ true if (sap_ha_pacemaker_cluster_corosync_totem is defined + and sap_ha_pacemaker_cluster_corosync_totem.options is defined + and sap_ha_pacemaker_cluster_corosync_totem | length > 0) else false }}" + + # Identify if __sap_ha_pacemaker_cluster_corosync_totem_platform is defined + __platform_totem_is_present: + "{{ true if (__sap_ha_pacemaker_cluster_corosync_totem_platform is defined + and __sap_ha_pacemaker_cluster_corosync_totem_platform.options is defined + and __sap_ha_pacemaker_cluster_corosync_totem_platform | length > 0) else false }}" + + __totem_settings: |- + {% if __user_totem_is_present %} + {% set corosync_totem = sap_ha_pacemaker_cluster_corosync_totem %} + {% elif __platform_totem_is_present %} + {% set corosync_totem = __sap_ha_pacemaker_cluster_corosync_totem_default + | combine(__sap_ha_pacemaker_cluster_corosync_totem_platform, recursive=True) %} + {% else %} + {% set corosync_totem = __sap_ha_pacemaker_cluster_corosync_totem_default %} + {% endif %} + {% set new_opts = [] %} + {% for option in corosync_totem.options | dict2items -%} + {%- set add_opts = new_opts.extend([ + { + 'name': option.key, + 'value': option.value + }]) -%} + {%- endfor %} + {{ new_opts }} + +# TODO: Add support for ha_cluster_quorum diff --git a/roles/sap_ha_pacemaker_cluster/tasks/construct_vars_webdisp.yml b/roles/sap_ha_pacemaker_cluster/tasks/construct_vars_webdisp.yml new file mode 100644 index 000000000..f35a73f57 --- /dev/null +++ b/roles/sap_ha_pacemaker_cluster/tasks/construct_vars_webdisp.yml @@ -0,0 +1,135 @@ +# SPDX-License-Identifier: Apache-2.0 +--- +# Variables containing variables must be constructed with values +# to be fed into the included ha_cluster role + +# - put here all scale-up and scale-out common resources +# - certain differences like ra agent names are provided through +# type specific variables + +# TODO: add conditionals to verify that the same resource agent is not already +# defined in user input variables. Conflicting user input should take precedence. + + +- name: "SAP HA Prepare Pacemaker - Add resource: Filesystem /usr/sap/<>/W<>" + ansible.builtin.set_fact: + __sap_ha_pacemaker_cluster_resource_primitives: "{{ __sap_ha_pacemaker_cluster_resource_primitives + [__resource_filesystem] }}" + vars: + __resource_filesystem: + id: "{{ sap_ha_pacemaker_cluster_wdp_filesystem_resource_name }}" + agent: "ocf:heartbeat:Filesystem" + instance_attrs: + - attrs: + - name: device + value: "{{ sap_ha_pacemaker_cluster_wdp_filesystem_host_mount_path }}" + - name: directory + value: "{{ sap_ha_pacemaker_cluster_wdp_filesystem_local_mount_path }}" + - name: fstype + value: "{{ sap_ha_pacemaker_cluster_wdp_filesystem_fstype }}" + - name: options + value: "{{ sap_ha_pacemaker_cluster_wdp_filesystem_options_string }}" + - name: force_unmount + value: "{{ sap_ha_pacemaker_cluster_wdp_filesystem_force_unmount }}" + operations: + - action: start + attrs: + - name: interval + value: 0 + - name: timeout + value: 60 + - action: stop + attrs: + - name: interval + value: 0 + - name: timeout + value: 120 + - action: monitor + attrs: + - name: interval + value: 200 + - name: timeout + value: 40 + when: + - __resource_filesystem.id not in (__sap_ha_pacemaker_cluster_resource_primitives | map(attribute='id')) + +- name: "SAP HA Prepare Pacemaker - Add resource: SAPInstance for SAP Web Dispatcher" + ansible.builtin.set_fact: + __sap_ha_pacemaker_cluster_resource_primitives: "{{ __sap_ha_pacemaker_cluster_resource_primitives + [__resource_sapinstance] }}" + vars: + __resource_sapinstance: + id: "{{ sap_ha_pacemaker_cluster_wdp_sapinstance_resource_name }}" + agent: "ocf:heartbeat:SAPInstance" + instance_attrs: + - attrs: + - name: InstanceName + value: "{{ sap_ha_pacemaker_cluster_wdp_sapinstance_instance_name }}" + - name: START_PROFILE + value: "{{ sap_ha_pacemaker_cluster_wdp_sapinstance_start_profile_string }}" + - name: AUTOMATIC_RECOVER + value: "{{ sap_ha_pacemaker_cluster_wdp_sapinstance_automatic_recover_bool | string }}" + - name: MONITOR_SERVICES + value: sapwebdisp + meta_attrs: + - attrs: + - name: resource-stickiness + value: "{{ sap_ha_pacemaker_cluster_wdp_sapinstance_resource_stickiness }}" + # - name: migration-threshold + # value: "{{ sap_ha_pacemaker_cluster_wdp_sapinstance_migration_threshold }}" + # - name: failure-timeout + # value: "{{ sap_ha_pacemaker_cluster_wdp_sapinstance_failure_timeout }}" + operations: + - action: start + attrs: + - name: interval + value: 0 + - name: timeout + value: 600 + - action: stop + attrs: + - name: interval + value: 0 + - name: timeout + value: 600 + - action: monitor + attrs: + - name: interval + value: 20 + - name: on-fail + value: restart + - name: timeout + value: 60 + when: + - __resource_sapinstance.id not in (__sap_ha_pacemaker_cluster_resource_primitives | map(attribute='id')) + +### Groups +# WebDisp group consists of resources in this order: +# - WebDisp filesystem +# - WebDisp VIP +# - WebDisp instance +# NOTE: WebDisp VIP must start before WebDisp instance as the generally binds to the VIP and will fail if the VIP is not available + +- name: "SAP HA Prepare Pacemaker - Add resource group for WebDisp resources" + ansible.builtin.set_fact: + __sap_ha_pacemaker_cluster_resource_groups: "{{ __sap_ha_pacemaker_cluster_resource_groups + [__webdisp_group] }}" + vars: + __webdisp_group: + id: "{{ sap_ha_pacemaker_cluster_vip_wdp_resource_group_name }}" + resource_ids: | + {% set resource_ids_list = [] %} + {%- for resource in + sap_ha_pacemaker_cluster_wdp_filesystem_resource_name, + sap_ha_pacemaker_cluster_vip_wdp_resource_name, + sap_ha_pacemaker_cluster_wdp_sapinstance_resource_name, + sap_ha_pacemaker_cluster_healthcheck_wdp_resource_name %} + {%- if resource | length > 0 + and resource in (__sap_ha_pacemaker_cluster_resource_primitives | map(attribute='id')) %} + {%- set ids = resource_ids_list.append(resource) %} + {%- endif %} + {%- endfor %} + {{ resource_ids_list }} + meta_attrs: + - attrs: + - name: resource-stickiness + value: "{{ sap_ha_pacemaker_cluster_wdp_group_stickiness }}" + when: + - __webdisp_group.id is not in (__sap_ha_pacemaker_cluster_resource_groups | map(attribute='id')) diff --git a/roles/sap_ha_pacemaker_cluster/tasks/include_construct_vip_resources.yml b/roles/sap_ha_pacemaker_cluster/tasks/include_construct_vip_resources.yml index 6a5517cb4..cc7fea0a5 100644 --- a/roles/sap_ha_pacemaker_cluster/tasks/include_construct_vip_resources.yml +++ b/roles/sap_ha_pacemaker_cluster/tasks/include_construct_vip_resources.yml @@ -52,6 +52,14 @@ sap_ha_pacemaker_cluster_healthcheck_nwas_abap_aas_port } if sap_ha_pacemaker_cluster_host_type | select('search', 'nwas_abap_pas_aas') | length > 0 else omit }}" + sap_webdisp: "{{ + { + sap_ha_pacemaker_cluster_vip_wdp_resource_name: + sap_ha_pacemaker_cluster_vip_wdp_ip_address | regex_replace('/.*', ''), + sap_ha_pacemaker_cluster_healthcheck_wdp_resource_name: + sap_ha_pacemaker_cluster_healthcheck_wdp_port + } if sap_ha_pacemaker_cluster_host_type | select('search', 'sap_webdisp') | length > 0 else omit }}" + ### Maintenance note # # The above task returns the following structure with a definition for HANA and ASCS/ERS, @@ -112,6 +120,7 @@ - "{{ __sap_ha_pacemaker_cluster_vip_nwas_ers_resource_name | d('') }}" - "{{ __sap_ha_pacemaker_cluster_vip_nwas_abap_pas_resource_name | d('') }}" - "{{ __sap_ha_pacemaker_cluster_vip_nwas_abap_aas_resource_name | d('') }}" + - "{{ sap_ha_pacemaker_cluster_vip_wdp_resource_name | d('') }}" __sap_ha_pacemaker_cluster_healthcheck_resource_list: - "{{ __sap_ha_pacemaker_cluster_healthcheck_hana_primary_resource_name | d('') }}" @@ -121,6 +130,7 @@ - "{{ __sap_ha_pacemaker_cluster_healthcheck_nwas_ers_resource_name | d('') }}" - "{{ __sap_ha_pacemaker_cluster_healthcheck_nwas_abap_pas_resource_name | d('') }}" - "{{ __sap_ha_pacemaker_cluster_healthcheck_nwas_abap_aas_resource_name | d('') }}" + - "{{ sap_ha_pacemaker_cluster_healthcheck_wdp_resource_name | d('') }}" # Repeat the VIP resource definition in a loop over the above combined possible parameters. # Applies to systems with no particular platform detected. diff --git a/roles/sap_ha_pacemaker_cluster/tasks/include_vars_webdisp.yml b/roles/sap_ha_pacemaker_cluster/tasks/include_vars_webdisp.yml new file mode 100644 index 000000000..016bc6108 --- /dev/null +++ b/roles/sap_ha_pacemaker_cluster/tasks/include_vars_webdisp.yml @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: Apache-2.0 +--- +# TEMP to fix CFLF +- name: SAP HA Prepare Pacemaker - Include Web Dispatcher landscape specific variables + ansible.builtin.include_vars: "{{ role_path }}/vars/{{ include_item }}.yml" + loop: "{{ __host_type_list | flatten }}" + loop_control: + loop_var: include_item + label: "{{ include_item }}.yml" + vars: + __host_type_list: + - nwas_common + - "{{ sap_ha_pacemaker_cluster_host_type }}" + when: + - "(role_path + '/vars/' + include_item + '.yml') is file" + + +# Prep for moving some of the vars from the main vars file here +# - name: "SAP HA Prepare Pacemaker - Set cluster resource variable: VIP Health Checks (WebDisp)" +# ansible.builtin.set_fact: +# __sap_ha_pacemaker_cluster_healthcheck_wdp_id: +# "{{ sap_ha_pacemaker_cluster_healthcheck_nwas_abap_ascs_id | d(__sap_ha_pacemaker_cluster_nwas_sid ~ 'ascs') +# if sap_ha_pacemaker_cluster_healthcheck_wdp_id | string | length == 0 +# else sap_ha_pacemaker_cluster_healthcheck_wdp_id }}" diff --git a/roles/sap_ha_pacemaker_cluster/tasks/main.yml b/roles/sap_ha_pacemaker_cluster/tasks/main.yml index 1c2ad75d3..1ea1e9249 100644 --- a/roles/sap_ha_pacemaker_cluster/tasks/main.yml +++ b/roles/sap_ha_pacemaker_cluster/tasks/main.yml @@ -298,6 +298,14 @@ when: - sap_ha_pacemaker_cluster_host_type | select('search', 'nwas_java_scs_ers') | length > 0 + - name: "SAP HA Install Pacemaker - Include SAP Web Dispatcher post steps" + ansible.builtin.include_tasks: + file: configure_webdisp_postinstallation.yml + apply: + tags: webdisp_postinst + tags: webdisp_postinst + when: + - sap_ha_pacemaker_cluster_host_type | select('search', 'sap_webdisp') | length > 0 ### END OF BLOCK: prerequisite changes and cluster setup diff --git a/roles/sap_ha_pacemaker_cluster/tasks/validate_input_parameters.yml b/roles/sap_ha_pacemaker_cluster/tasks/validate_input_parameters.yml index dc233b063..c7a38ff80 100644 --- a/roles/sap_ha_pacemaker_cluster/tasks/validate_input_parameters.yml +++ b/roles/sap_ha_pacemaker_cluster/tasks/validate_input_parameters.yml @@ -39,6 +39,38 @@ when: - sap_ha_pacemaker_cluster_host_type | select('search', 'nwas') | length > 0 +- name: "SAP HA Prepare Pacemaker - (SAP WebDisp) Validate WebDisp System ID" + ansible.builtin.assert: + that: + - sap_ha_pacemaker_cluster_wdp_sid | length == 3 + - sap_ha_pacemaker_cluster_wdp_sid not in __sap_ha_pacemaker_cluster_sid_prohibited + fail_msg: | + Host type = {{ sap_ha_pacemaker_cluster_host_type }} + Requires 'sap_ha_pacemaker_cluster_wdp_sid' to be defined! + when: + - sap_ha_pacemaker_cluster_host_type | select('search', 'sap_webdisp') | length > 0 + +- name: "SAP HA Prepare Pacemaker - (SAP WebDisp) Validate WebDisp Instance Number" + ansible.builtin.assert: + that: + - ( + sap_ha_pacemaker_cluster_wdp_instance_nr | type_debug != 'int' + and sap_ha_pacemaker_cluster_wdp_instance_nr | length == 2 + ) + or + ( + sap_ha_pacemaker_cluster_wdp_instance_nr | type_debug == 'int' + and ssap_ha_pacemaker_cluster_wdp_instance_nr is regex("^[0-9][0-9]$") + ) + fail_msg: | + + Host type = {{ sap_ha_pacemaker_cluster_host_type }} + Requires 'sap_ha_pacemaker_cluster_wdp_instance_nr' to be defined. + + The instance number must be exactly 2 digits. + Add quotes if the number starts with a 0! + when: + - sap_ha_pacemaker_cluster_host_type | select('search', 'sap_webdisp') | length > 0 # Validate SAP Instance Number - name: "SAP HA Prepare Pacemaker - (SAP NetWeaver) Validate SAP Instance Number" @@ -220,3 +252,12 @@ fail_msg: "Host type = '{{ sap_ha_pacemaker_cluster_host_type }}', but 'sap_ha_pacemaker_cluster_vip_nwas_abap_aas_ip_address' is not defined." when: - sap_ha_pacemaker_cluster_host_type | select('search', 'nwas_abap_pas_aas') | length > 0 + +- name: "SAP HA Prepare Pacemaker - (SAP WebDisp) Verify that the VIP is defined" + ansible.builtin.assert: + that: + - sap_ha_pacemaker_cluster_vip_wdp_ip_address is defined + - sap_ha_pacemaker_cluster_vip_wdp_ip_address | length > 0 + fail_msg: "Host type = '{{ sap_ha_pacemaker_cluster_host_type }}', but 'sap_ha_pacemaker_cluster_vip_wdp_ip_address' is not defined." + when: + - sap_ha_pacemaker_cluster_host_type | select('search', 'sap_webdisp') | length > 0 diff --git a/roles/sap_ha_pacemaker_cluster/vars/main.yml b/roles/sap_ha_pacemaker_cluster/vars/main.yml index 7bc19eef0..6f321b7c7 100644 --- a/roles/sap_ha_pacemaker_cluster/vars/main.yml +++ b/roles/sap_ha_pacemaker_cluster/vars/main.yml @@ -56,6 +56,7 @@ sap_ha_pacemaker_cluster_healthcheck_nwas_ers_port: "{{ sap_ha_pacemaker_cluster_healthcheck_nwas_abap_ers_port | d('') }}" sap_ha_pacemaker_cluster_healthcheck_nwas_abap_pas_port: '' sap_ha_pacemaker_cluster_healthcheck_nwas_abap_aas_port: '' +sap_ha_pacemaker_cluster_healthcheck_wdp_port: '' # (cloud) platform helper variable - leave empty for default = not cloud __sap_ha_pacemaker_cluster_platform: '' diff --git a/roles/sap_ha_pacemaker_cluster/vars/sap_webdisp.yml b/roles/sap_ha_pacemaker_cluster/vars/sap_webdisp.yml new file mode 100644 index 000000000..a47499cbc --- /dev/null +++ b/roles/sap_ha_pacemaker_cluster/vars/sap_webdisp.yml @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: Apache-2.0 +--- +# The following directories are appended to the 'nfs_path' of the '/usr/sap' storage +# definition. +# Therefore, the /usr/sap prefix must be left out of the listed path items. +__sap_ha_pacemaker_cluster_wdp_filesystems: + - "{{ sap_ha_pacemaker_cluster_wdp_sid }}/W{{ sap_ha_pacemaker_cluster_wdp_instance_nr }}" + + +# List of WDP profile names. +# Used in tasks/configure_nwas_postinstallation.yml for sap_cluster_connector setup. +__sap_ha_pacemaker_cluster_wdp_profile_paths: + - "{{ sap_ha_pacemaker_cluster_wdp_sapinstance_start_profile_string }}"