diff --git a/roles/sap_vm_temp_vip/INPUT_PARAMETERS.md b/roles/sap_vm_temp_vip/INPUT_PARAMETERS.md new file mode 100644 index 0000000..6ef4192 --- /dev/null +++ b/roles/sap_vm_temp_vip/INPUT_PARAMETERS.md @@ -0,0 +1,66 @@ +## Input Parameters for sap_vm_temp_vip Ansible Role + +### sap_vm_temp_vip_default_ip + +- _Type:_ `string` +- _Default:_ `ansible_default_ipv4.address` + +IP Address of default network interface is obtained from Ansible Facts and it is used for calculation of missing input parameters. + +### sap_vm_temp_vip_default_netmask + +- _Type:_ `string` +- _Default:_ `ansible_default_ipv4.netmask` + +Netmask of default network interface is obtained from Ansible Facts and it is used for calculation of missing input parameters. + +### sap_vm_temp_vip_default_prefix + +- _Type:_ `string` +- _Default:_ `ansible_default_ipv4.prefix` + +Prefix of default network interface is obtained from Ansible Facts and it is used for calculation of missing input parameters. + +### sap_vm_temp_vip_default_broadcast + +- _Type:_ `string` +- _Default:_ `ansible_default_ipv4.broadcast` + +Broadcast of default network interface is obtained from Ansible Facts and it is used for calculation of missing input parameters.
+This parameter is empty on some cloud platforms and VIP is created without broadcast if attempt to calculate fails. + +### sap_vm_temp_vip_default_interface + +- _Type:_ `string` +- _Default:_ `ansible_default_ipv4.interface` or `eth0` + +Default Network Interface name is obtained from Ansible Facts and it is used for calculation of missing input parameters.
+Ensure to use correct Network Interface if default interface from Ansible Facts does not represent desired Network Interface. + +### sap_vm_temp_vip_hana_primary +- _Type:_ `string` +- _Default:_ `sap_ha_pacemaker_cluster_vip_hana_primary_ip_address` + +Mandatory for SAP HANA cluster setup.
+VIP address is by default assigned from `sap_ha_pacemaker_cluster_vip_hana_primary_ip_address` input parameter used by [sap_ha_pacemaker_cluster](https://github.com/sap-linuxlab/community.sap_install/tree/main/roles/sap_ha_pacemaker_cluster) role. + +### sap_vm_temp_vip_nwas_abap_ascs +- _Type:_ `string` +- _Default:_ `sap_ha_pacemaker_cluster_vip_nwas_abap_ascs_ip_address` + +Mandatory for SAP ASCS/ERS cluster setup.
+VIP address is by default assigned from `sap_ha_pacemaker_cluster_vip_nwas_abap_ascs_ip_address` input parameter used by [sap_ha_pacemaker_cluster](https://github.com/sap-linuxlab/community.sap_install/tree/main/roles/sap_ha_pacemaker_cluster) role. + +### sap_vm_temp_vip_nwas_abap_ers +- _Type:_ `string` +- _Default:_ `sap_ha_pacemaker_cluster_vip_nwas_abap_ers_ip_address` + +Mandatory for SAP ASCS/ERS cluster setup.
+VIP address is by default assigned from `sap_ha_pacemaker_cluster_vip_hana_primary_ip_address` input parameter used by [sap_ha_pacemaker_cluster](https://github.com/sap-linuxlab/community.sap_install/tree/main/roles/sap_ha_pacemaker_cluster) role. + +### sap_vm_temp_vip_anydb_primary +- _Type:_ `string` + +Mandatory for SAP AnyDB cluster setup. + + \ No newline at end of file diff --git a/roles/sap_vm_temp_vip/README.md b/roles/sap_vm_temp_vip/README.md index c72b8b0..bd7e40c 100644 --- a/roles/sap_vm_temp_vip/README.md +++ b/roles/sap_vm_temp_vip/README.md @@ -1,81 +1,77 @@ -`Beta` - + # sap_vm_temp_vip Ansible Role + -Ansible Role for assignment of Temporary Virtual IP (VIP) to OS Network Interface prior to Linux Pacemaker ownership. - -This Ansible Role will (dependent on detected Infrastructure Platform) perform assignment of a Virtual IP Address to the OS Network Interface. - - -## Functionality - -The hosts for SAP Software allocated for High Availability are configured with a temporary Virtual IP for the OS Network Interface; thereby allowing Linux Pacemaker to be installed once the SAP Software installation has concluded (best practice for Linux Pacemaker). When an Infrastructure Platform with specific requirements is detected (e.g. Load Balancers), then bespoke actions are performed. - - -## Scope +## Description + +Ansible role `sap_vm_temp_vip` is used to enable installation of SAP Application and Database on High Availability clusters provisioned by [sap_vm_provision](https://github.com/sap-linuxlab/community.sap_infrastructure/tree/main/roles/sap_vm_provision) role. -Only hosts required for High Availability (such as SAP HANA Primary node, SAP NetWeaver ASCS/ERS) should use this Ansible Role. +Installation of cluster environment requires temporary assignment of Virtual IP (VIP) before executing installation roles [sap_hana_install](https://github.com/sap-linuxlab/community.sap_install/tree/main/roles/sap_hana_install) and [sap_swpm](https://github.com/sap-linuxlab/community.sap_install/tree/main/roles/sap_swpm). +- This is temporary and it will be replaced by Cluster VIP resource once cluster is configured by [sap_ha_pacemaker_cluster](https://github.com/sap-linuxlab/community.sap_install/tree/main/roles/sap_ha_pacemaker_cluster) role. -Assumptions are made based upon the default High Availability configuration for a given Infrastructure Platform (e.g. using Linux Pacemaker `IPAddr2` resource agent). +This role does not update `/etc/hosts` or DNS records, as these steps are performed by the [sap_vm_provision](https://github.com/sap-linuxlab/community.sap_infrastructure/tree/main/roles/sap_vm_provision) role. + +## Prerequisites + +Environment: +- Assign hosts to correct groups, which are also used in other roles in our project + - Supported cluster groups: `hana_primary, hana_secondary, anydb_primary, anydb_secondary, nwas_ascs, nwas_ers` -## Requirements - -### Target hosts - -**OS Versions:** -- Red Hat Enterprise Linux 8.2+ -- SUSE Linux Enterprise Server 15 SP3+ - -### Execution/Controller host - -**Dependencies:** -- OS Packages - - Python 3.9.7+ (i.e. CPython distribution) -- Python Packages - - None -- Ansible - - Ansible Core 2.12.0+ - - Ansible Collections: - - None - +Role dependency: +- [sap_vm_provision](https://github.com/sap-linuxlab/community.sap_infrastructure/tree/main/roles/sap_vm_provision), for creating required resources: DNS, Load Balancers and Health Checks. + ## Execution - -### Sample execution - -For further information, see the [sample Ansible Playbooks in `/playbooks`](../playbooks/). - -### Suggested execution sequence - -It is advised this Ansible Role is used only for High Availability and executed prior to execution of: -- sap_hana_install -- sap_swpm - -Prior to execution of this Ansible Role, there are no Ansible Roles suggested to be executed first. - -### Summary of execution flow - -- Identify IPv4 Address with CIDR and Broadcast Address -- If SAP AnyDB or SAP NetWeaver, assign Virtual IP to OS Network Interface. If SAP HANA, skip -- Start temporary listener for SAP HANA, SAP AnyDB or SAP NetWeaver when using Load Balancers _(GCP, IBM Cloud, MS Azure)_ - -### Tags to control execution - -There are no tags used to control the execution of this Ansible Role - + +Role can be execute separately or as part of [ansible.playbooks_for_sap](https://github.com/sap-linuxlab/ansible.playbooks_for_sap) playbooks. + + +### Execution Flow + +1. Assert that required inputs were provided. +2. Collect missing inputs using provided inputs (example: Calculate prefix from netmask, if VIP prefix was not defined) +3. Append VIP to network interface + - SAP HANA Primary host if both groups are present: `hana_primary, hana_secondary` + - SAP AnyDB Primary host if both groups are present: `anydb_primary, anydb_secondary` + - SAP ASCS host if both groups are present: `nwas_ascs, nwas_ers` + - SAP ERS host if both groups are present:` nwas_ascs, nwas_ers` +4. Install `netcat` and start 12 hour process to ensure that Load Balancer Health Checks are working before Cluster is configured. + - Limited to platforms with Network Load Balancers and `IPAddr2` resource agent: Google Cloud, MS Azure, IBM Cloud. + + +### Example + +```yaml +- name: Ansible Play for Temporary VIP setup on SAP ASCS/ERS hosts + hosts: nwas_ascs, nwas_ers + become: true + any_errors_fatal: true + max_fail_percentage: 0 + tasks: + + - name: Execute Ansible Role sap_vm_temp_vip + ansible.builtin.include_role: + name: community.sap_infrastructure.sap_vm_temp_vip +``` + + + + + + + ## License - + Apache 2.0 + +## Maintainers + +- [Sean Freeman](https://github.com/sean-freeman) +- [Marcel Mamula](https://github.com/marcelmamula) + -## Authors - -Sean Freeman - ---- - -## Ansible Role Input Variables - -Please first check the [/defaults parameters file](./defaults/main.yml). +## Role Input Parameters +All input parameters used by role are described in [INPUT_PARAMETERS.md](https://github.com/sap-linuxlab/community.sap_infrastructure/blob/main/roles/sap_vm_temp_vip/INPUT_PARAMETERS.md) diff --git a/roles/sap_vm_temp_vip/defaults/main.yml b/roles/sap_vm_temp_vip/defaults/main.yml index 8ab3cd8..df7e520 100644 --- a/roles/sap_vm_temp_vip/defaults/main.yml +++ b/roles/sap_vm_temp_vip/defaults/main.yml @@ -1,5 +1,13 @@ --- +# General variables that are calculated from Ansible facts +sap_vm_temp_vip_default_ip: "{{ ansible_default_ipv4.address | default('') }}" +sap_vm_temp_vip_default_netmask: "{{ ansible_default_ipv4.netmask | default('') }}" +sap_vm_temp_vip_default_prefix: "{{ ansible_default_ipv4.prefix | default('') }}" +sap_vm_temp_vip_default_broadcast: "{{ ansible_default_ipv4.broadcast | default('') }}" +sap_vm_temp_vip_default_interface: "{{ ansible_default_ipv4.interface | default('eth0') }}" + +# SAP specific IPs are defined from sap_install.sap_ha_pacemaker_role input variables sap_vm_temp_vip_hana_primary: "{{ sap_ha_pacemaker_cluster_vip_hana_primary_ip_address | default('') }}" sap_vm_temp_vip_anydb_primary: "" sap_vm_temp_vip_nwas_abap_ascs: "{{ sap_ha_pacemaker_cluster_vip_nwas_abap_ascs_ip_address | default('') }}" diff --git a/roles/sap_vm_temp_vip/tasks/get_temp_vip_details.yml b/roles/sap_vm_temp_vip/tasks/get_temp_vip_details.yml new file mode 100644 index 0000000..1ed03ab --- /dev/null +++ b/roles/sap_vm_temp_vip/tasks/get_temp_vip_details.yml @@ -0,0 +1,100 @@ +--- +# Get details of default ip route to detect default network interface +- name: Get network interface from ip route show default 0.0.0.0/0 + ansible.builtin.shell: + cmd: set -o pipefail && ip route show default 0.0.0.0/0 | awk '/default/ {print $5}' + register: __sap_vm_temp_vip_get_route + changed_when: false + failed_when: false + +# Get content of ip address show filtered by primary IP +- name: Get contents of ip address show for {{ sap_vm_temp_vip_default_ip }} + ansible.builtin.shell: + cmd: set -o pipefail && ip -oneline address show {{ __sap_vm_temp_vip_get_route.stdout }} | grep {{ sap_vm_temp_vip_default_ip }} + when: + - __sap_vm_temp_vip_get_route.stdout is defined and __sap_vm_temp_vip_get_route.stdout | length > 0 + register: __sap_vm_temp_vip_get_ips + changed_when: false + failed_when: false + +# Extract prefix from netmask if it is available +# Use localhost (execution host) Python3 instead of relying on target host +- name: Calculate prefix from netmask {{ sap_vm_temp_vip_default_netmask }} + delegate_to: localhost + ansible.builtin.command: + cmd: > + python3 -c "import ipaddress; print(ipaddress.IPv4Network('{{ sap_vm_temp_vip_default_ip }}/{{ sap_vm_temp_vip_default_netmask }}', strict=False).prefixlen)" + when: + - sap_vm_temp_vip_default_prefix == '' + - sap_vm_temp_vip_default_netmask | length > 0 + register: __sap_vm_temp_vip_get_prefix_netmask + changed_when: false + failed_when: false + +# Extract prefix from primary IP on default interface if netmask is not available +# Stdout result is array instead of string. [0] is used to select only one in case of multiple results. +# [0] could be replaced by join('') but it would require duplicate record validation. +- name: Calculate prefix from IP {{ sap_vm_temp_vip_default_ip }} if sap_vm_temp_vip_default_netmask is empty + ansible.builtin.set_fact: + __sap_vm_temp_vip_get_prefix_ip: + "{{ (__sap_vm_temp_vip_inet[0] | basename) if __sap_vm_temp_vip_inet | length > 0 else __sap_vm_temp_vip_inet }}" + vars: + __sap_vm_temp_vip_inet: "{{ __sap_vm_temp_vip_get_ips.stdout | regex_search('inet ([0-9.]+/[0-9]+)', '\\1') }}" + when: + - sap_vm_temp_vip_default_prefix == '' + - sap_vm_temp_vip_default_netmask == '' + - __sap_vm_temp_vip_get_ips is defined and __sap_vm_temp_vip_get_ips.stdout is defined and __sap_vm_temp_vip_get_ips.stdout | length > 0 + changed_when: false + + +# Combine final prefix variable based on decision below: +# 1. Always use /32 for AWS and GCP, regardless of existing prefix +# 2. Else use prefix calculated from netmask if it is available and sap_vm_temp_vip_default_prefix is empty +# 3. Else use prefix calculated from primary IP if netmask is not available and sap_vm_temp_vip_default_prefix is empty +# 4. Else use sap_vm_temp_vip_default_prefix (regardless of content) to be used to skip steps. +- name: Update netmask prefix variable if it was calculated + ansible.builtin.set_fact: + __sap_vm_temp_vip_prefix: >- + {%- if __sap_vm_temp_vip_force_static_32 -%} + 32 + {%- elif sap_vm_temp_vip_default_prefix | length == 0 + and __sap_vm_temp_vip_get_prefix_netmask.stdout is defined and __sap_vm_temp_vip_get_prefix_netmask.stdout | length > 0 -%} + {{ __sap_vm_temp_vip_get_prefix_netmask.stdout }} + {%- elif sap_vm_temp_vip_default_prefix | length == 0 + and __sap_vm_temp_vip_get_prefix_ip is defined and __sap_vm_temp_vip_get_prefix_ip | length > 0 -%} + {{ __sap_vm_temp_vip_get_prefix_ip }} + {%- else -%} + {{ sap_vm_temp_vip_default_prefix }} + {%- endif -%} + vars: + __sap_vm_temp_vip_force_static_32: + "{{ true if (('amazon' in (ansible_system_vendor | lower) or 'amazon' in (ansible_product_name | lower)) + or (ansible_product_name == 'Google Compute Engine')) else false }}" + + +# Extract broadcast IP from primary IP if it is present and ansible fact ansible_default_ipv4.broadcast is empty +# Stdout result is array instead of string. [0] is used to select only one in case of multiple results. +# [0] could be replaced by join('') but it would require duplicate record validation. +- name: Calculate broadcast IP from IP {{ sap_vm_temp_vip_default_ip }} if sap_vm_temp_vip_default_broadcast is empty + ansible.builtin.set_fact: + __sap_vm_temp_vip_get_broadcast_ip: + "{{ (__sap_vm_temp_vip_brd[0] | basename) if __sap_vm_temp_vip_brd | length > 0 else __sap_vm_temp_vip_brd }}" + vars: + __sap_vm_temp_vip_brd: "{{ __sap_vm_temp_vip_get_ips.stdout | regex_search('brd ([0-9.]+)', '\\1') }}" + when: + - sap_vm_temp_vip_default_broadcast == '' + - __sap_vm_temp_vip_get_ips is defined and __sap_vm_temp_vip_get_ips.stdout is defined and __sap_vm_temp_vip_get_ips.stdout | length > 0 + changed_when: false + +# Combine final broadcast IP based on decision below: +# 1. Use calculated broadcast from primary IP if sap_vm_temp_vip_default_broadcast is empty +# 2. Else use sap_vm_temp_vip_default_broadcast (regardless of content) to be used during VIP creation +- name: Update broadcast IP variable if it was calculated + ansible.builtin.set_fact: + __sap_vm_temp_vip_broadcast: >- + {%- if sap_vm_temp_vip_default_broadcast | length == 0 + and __sap_vm_temp_vip_get_broadcast_ip is defined and __sap_vm_temp_vip_get_broadcast_ip | length > 0 -%} + {{ __sap_vm_temp_vip_get_broadcast_ip }} + {%- else -%} + {{ sap_vm_temp_vip_default_broadcast }} + {%- endif -%} diff --git a/roles/sap_vm_temp_vip/tasks/main.yml b/roles/sap_vm_temp_vip/tasks/main.yml index cdcd579..e1d4565 100644 --- a/roles/sap_vm_temp_vip/tasks/main.yml +++ b/roles/sap_vm_temp_vip/tasks/main.yml @@ -1,14 +1,42 @@ --- +# Ansible role to setup temporary Virtual IP (VIP) -- name: Setup temporary Virtual IP (VIP) +- name: Collect Ansible facts for required subsets - Hardware and Network Interfaces + ansible.builtin.setup: + gather_subset: + - hardware + - interfaces + +- name: Assert that sap_vm_temp_vip_default_ip is defined + ansible.builtin.assert: + that: sap_vm_temp_vip_default_ip is defined and sap_vm_temp_vip_default_ip | length > 0 + fail_msg: + - "Unable to get ansible fact ansible_default_ipv4.address or variable sap_vm_temp_vip_default_ip is empty!" + - "Ensure that sap_vm_temp_vip_default_ip is not empty." + + +- name: Block to ensure that only supported groups are allowed + when: group_names | intersect(['hana_primary', 'hana_secondary', 'anydb_primary', 'anydb_secondary', 'nwas_ascs', 'nwas_ers']) block: # - name: Identify OS Primary Network Interface # ansible.builtin.include_tasks: "identify_network_interface.yml" + - name: Attempt to obtain missing network information + ansible.builtin.include_tasks: "get_temp_vip_details.yml" + when: + - not ansible_chassis_asset_tag == 'ibmcloud' # Moved here instead of each task inside of "set_temp_vip.yml" + + - name: Execute temporary set of a Virtual IP (VIP) prior to Linux Pacemaker ownership ansible.builtin.include_tasks: "set_temp_vip.yml" + when: + - not ansible_chassis_asset_tag == 'ibmcloud' # Moved here instead of each task inside of "set_temp_vip.yml" + + # Required when using Load Balancers (i.e. Google Cloud, IBM Cloud, MS Azure) - name: Set Health Check Probe Listener for Virtual IP when Load Balancer ansible.builtin.include_tasks: "set_temp_vip_lb_listener.yml" - when: (ansible_product_name == 'Google Compute Engine') or (ansible_chassis_asset_tag == 'ibmcloud') or (ansible_chassis_vendor == 'Microsoft Corporation' and ansible_product_name == 'Virtual Machine') + when: + - (ansible_product_name == 'Google Compute Engine') or (ansible_chassis_asset_tag == 'ibmcloud') + or (ansible_chassis_vendor == 'Microsoft Corporation' and ansible_product_name == 'Virtual Machine') diff --git a/roles/sap_vm_temp_vip/tasks/set_temp_vip.yml b/roles/sap_vm_temp_vip/tasks/set_temp_vip.yml index c8014cc..546b800 100644 --- a/roles/sap_vm_temp_vip/tasks/set_temp_vip.yml +++ b/roles/sap_vm_temp_vip/tasks/set_temp_vip.yml @@ -17,242 +17,117 @@ ## Set Virtual IP - Other related information # In all cases, use noprefixroute parameter to avoid automatic creation of OS route table entries (i.e. 'ip route'), which occurs if the IP Address is outside of the existing Subnet Range +# TODO: Add rare scenario for PAS/AAS VIP if needed. +# (groups["nwas_pas"] is defined and inventory_hostname in groups["nwas_pas"]) and (groups["nwas_pas"] is defined and (groups["nwas_pas"]|length>0)) -- name: Set fact for Broadcast Address and Prefix of the Primary IP - Generic - ansible.builtin.set_fact: - primary_ip_address: "{{ ansible_default_ipv4.address | default('') }}" - primary_ip_address_netmask: "{{ ansible_default_ipv4.netmask | default('') }}" - primary_ip_address_netmask_cidr_prefix: "{{ ansible_default_ipv4.prefix | default('') }}" - primary_ip_broadcast_address: "{{ ansible_default_ipv4.broadcast | default('') }}" -- name: Install ipcalc binary when required - ansible.builtin.package: - name: - - ipcalc - state: present +# Define VIP address based on target host group which is filtered in main.yml +- name: Set fact for VIP address depending on target host group + ansible.builtin.set_fact: + __sap_vm_temp_vip_address: >- + {% if groups['hana_secondary'] | d([]) | length > 0 and inventory_hostname in groups["hana_primary"] -%} + {{ sap_vm_temp_vip_hana_primary | regex_replace('/.*', '') }} + {%- elif groups['anydb_secondary'] | d([]) | length > 0 and inventory_hostname in groups["anydb_primary"] -%} + {{ sap_vm_temp_vip_anydb_primary | regex_replace('/.*', '') }} + {%- elif groups["nwas_ers"] | d([]) | length > 0 and inventory_hostname in groups["nwas_ascs"] -%} + {{ sap_vm_temp_vip_nwas_abap_ascs | regex_replace('/.*', '') }} + {%- elif groups["nwas_ers"] | d([]) | length > 0 and inventory_hostname in groups["nwas_ers"] -%} + {{ sap_vm_temp_vip_nwas_abap_ers | regex_replace('/.*', '') }} + {%- endif %} + +# Get content of ip address show filtered by VIP +- name: Get contents of ip address show for {{ __sap_vm_temp_vip_address }} + ansible.builtin.shell: + cmd: set -o pipefail && ip -oneline address show | grep {{ __sap_vm_temp_vip_address }} when: - - not primary_ip_broadcast_address == "" - - primary_ip_address_netmask_cidr_prefix == "" - - primary_ip_address_netmask != "" - -- name: Show temporary Virtual IP actions + - __sap_vm_temp_vip_address is defined and __sap_vm_temp_vip_address | length > 0 + register: __sap_vm_temp_vip_get_vip + changed_when: false + ignore_errors: true + failed_when: false + + +# Show debug information if existing VIP is found based on decision below: +# 1. Inform that found VIP is same as VIP planned to create +# 2. Else inform that VIP found is under different prefix than planned to create +# 3. Else inform that more than one VIP was found +# 4. Else inform that comparison failed because provided prefix was empty +# join('') is used instead of [0] because duplicate records are filtered out +- name: Show information if VIP is already present on network interfaces ansible.builtin.debug: - msg: | - Ansible Facts: - primary_ip_address = "{{ ansible_default_ipv4.address | default('') }}" - primary_ip_address_netmask = "{{ ansible_default_ipv4.netmask | default('') }}" - primary_ip_address_netmask_cidr_prefix = "{{ ansible_default_ipv4.prefix | default('') }}" - primary_ip_broadcast_address = "{{ ansible_default_ipv4.broadcast | default('') }}" - - Command to be executed: - {% if (('amazon' in (ansible_system_vendor | lower) or 'amazon' in (ansible_product_name | lower)) or (ansible_product_name == 'Google Compute Engine')) %} - ip address add VIRTUAL_IP_ADDRESS/32 brd {{ primary_ip_broadcast_address }} dev eth0 noprefixroute - {% elif primary_ip_broadcast_address == "" and primary_ip_address_netmask_cidr_prefix == "" %} - ip address add VIRTUAL_IP_ADDRESS/32 brd + dev eth0 noprefixroute - {% elif (primary_ip_broadcast_address != "" and primary_ip_address_netmask_cidr_prefix == "") and primary_ip_address_netmask != "" %} - ip address add VIRTUAL_IP_ADDRESS/NETMASK_PREFIX brd {{ primary_ip_broadcast_address }} dev eth0 noprefixroute - {% elif ((primary_ip_broadcast_address != "" and primary_ip_address_netmask_cidr_prefix == "") and primary_ip_address_netmask == "") and primary_ip_address != "" %} - ip address add VIRTUAL_IP_ADDRESS/NETMASK_PREFIX brd {{ primary_ip_broadcast_address }} dev eth0 noprefixroute - {% elif primary_ip_broadcast_address == "" and primary_ip_address_netmask_cidr_prefix != "" %} - ip address add VIRTUAL_IP_ADDRESS/{{ primary_ip_address_netmask_cidr_prefix }} brd + dev eth0 noprefixroute - {% elif primary_ip_broadcast_address != "" and primary_ip_address_netmask_cidr_prefix != "" %} - ip address add VIRTUAL_IP_ADDRESS/{{ primary_ip_address_netmask_cidr_prefix }} brd {{ primary_ip_broadcast_address }} dev eth0 noprefixroute - {% else %} - ERROR - {% endif %} + msg: >- + {%- if __vip_expected == __vip_found -%} + VIP address {{ __vip_expected }} is already present. VIP creation will be skipped. + {%- elif __vip_expected != __vip_found and __sap_vm_temp_vip_prefix != '' and not __vip_multiple -%} + VIP address {{ __vip_expected }} is already present with different prefix {{ __vip_found }}. VIP creation will be skipped. + {%- elif __vip_multiple -%} + Multiple VIP address entries found. VIP creation will be skipped. + {%- else -%} + VIP address {{ __sap_vm_temp_vip_address }} is already present, but comparison failed because of empty sap_vm_temp_vip_default_prefix. + {%- endif -%} + vars: + __vip_expected: "{{ __sap_vm_temp_vip_address ~ '/' ~ __sap_vm_temp_vip_prefix }}" + __vip_found: "{{ __sap_vm_temp_vip_get_vip.stdout | regex_search('inet ([0-9.]+/[0-9]+)', '\\1') | join('') if not __vip_multiple else '' }}" + __vip_multiple: "{{ true if __sap_vm_temp_vip_get_vip.stdout_lines | length > 1 else false }}" when: - - (groups["hana_secondary"] is defined and inventory_hostname in groups["hana_primary"]) or (groups["anydb_secondary"] is defined and inventory_hostname in groups["anydb_primary"]) or (groups["nwas_ers"] is defined and (inventory_hostname in groups["nwas_ascs"] or inventory_hostname in groups["nwas_ers"])) - - not ansible_chassis_asset_tag == 'ibmcloud' + - __sap_vm_temp_vip_get_vip.stdout is defined and __sap_vm_temp_vip_get_vip.stdout | length > 0 + - __sap_vm_temp_vip_address is defined and __sap_vm_temp_vip_address | length > 0 -#### HA of HANA Primary/Secondary #### - -# Not required before SAP HANA installation or Linux Pacemaker installation, performed so the VIP connectivity can be tested -- name: Append temporary Virtual IP (VIP) to network interface for SAP HANA, will be replaced by Linux Pacemaker IPaddr2 Resource Agent - ansible.builtin.shell: | - platform_cidr_32_static="{{ 'true' if (('amazon' in (ansible_system_vendor | lower) or 'amazon' in (ansible_product_name | lower)) or (ansible_product_name == 'Google Compute Engine')) }}" - if [ "$platform_cidr_32_static" = "true" ] && [ "{{ primary_ip_broadcast_address }}" = "" ] - then - ip address add {{ sap_vm_temp_vip_hana_primary | regex_replace('/.*', '') }}/32 dev eth0 noprefixroute - elif [ "$platform_cidr_32_static" = "true" ] && [ "{{ primary_ip_broadcast_address }}" != "" ] - then - ip address add {{ sap_vm_temp_vip_hana_primary | regex_replace('/.*', '') }}/32 brd {{ primary_ip_broadcast_address }} dev eth0 noprefixroute - elif [ "{{ primary_ip_broadcast_address }}" = "" ] && [ "{{ primary_ip_address_netmask_cidr_prefix }}" = "" ] - then - ip address add {{ sap_vm_temp_vip_hana_primary | regex_replace('/.*', '') }}/32 brd + dev eth0 noprefixroute - elif [ "{{ primary_ip_broadcast_address }}" != "" ] && [ "{{ primary_ip_address_netmask_cidr_prefix }}" = "" ] && [ "{{ primary_ip_address_netmask }}" != "" ] - then - calculate_netmask_prefix=$(ipcalc --prefix 0.0.0.0 {{ primary_ip_address_netmask }}) - ip address add {{ sap_vm_temp_vip_hana_primary | regex_replace('/.*', '') }}/$calculate_netmask_prefix brd {{ primary_ip_broadcast_address }} dev eth0 noprefixroute - elif [ "{{ primary_ip_broadcast_address }}" != "" ] && [ "{{ primary_ip_address_netmask_cidr_prefix }}" = "" ] && [ "{{ primary_ip_address_netmask }}" == "" ] && [ "{{ primary_ip_address }}" != "" ] - then - discover_network_adapter_active=$(ip route show default 0.0.0.0/0 | awk '/default/ {print $5}') - discover_primary_ip_cidr=$(ip -oneline address show $discover_network_adapter_active | grep {{ primary_ip_address }} | sed -n 's/.*inet \(.*\) brd.*/\1/p') - discover_netmask_prefix=$(basename $discover_primary_ip_cidr) - ip address add {{ sap_vm_temp_vip_hana_primary | regex_replace('/.*', '') }}/$discover_netmask_prefix brd {{ primary_ip_broadcast_address }} dev eth0 noprefixroute - elif [ "{{ primary_ip_broadcast_address }}" = "" ] && "{{ primary_ip_address_netmask_cidr_prefix }}" != "" ] - then - discover_network_adapter_active=$(ip route show default 0.0.0.0/0 | awk '/default/ {print $5}') - discover_primary_ip_bridge=$(ip -oneline address show $discover_network_adapter_active | grep {{ primary_ip_address }} | sed -n 's/.*brd \(.*\) scope.*/\1/p') - ip address add {{ sap_vm_temp_vip_hana_primary | regex_replace('/.*', '') }}/{{ primary_ip_address_netmask_cidr_prefix }} brd + dev eth0 noprefixroute - #ip address add {{ sap_vm_temp_vip_hana_primary | regex_replace('/.*', '') }}/{{ primary_ip_address_netmask_cidr_prefix }} brd $discover_primary_ip_bridge dev eth0 noprefixroute - elif [ "{{ primary_ip_broadcast_address }}" != "" ] && [ "{{ primary_ip_address_netmask_cidr_prefix }}" != "" ] - then - ip address add {{ sap_vm_temp_vip_hana_primary | regex_replace('/.*', '') }}/{{ primary_ip_address_netmask_cidr_prefix }} brd {{ primary_ip_broadcast_address }} dev eth0 noprefixroute - else - exit 1 - fi - when: - - (groups["hana_secondary"] is defined and inventory_hostname in groups["hana_primary"]) and (groups["hana_secondary"] is defined and (groups["hana_secondary"]|length>0)) - - not ansible_chassis_asset_tag == 'ibmcloud' - ignore_errors: true # Ignore when stderr is 'RTNETLINK answers: File exists' - -# Not required before SAP HANA installation or Linux Pacemaker installation, performed so the VIP connectivity can be tested -- name: Append temporary Virtual IP (VIP) to network interface for SAP AnyDB, will be replaced by Linux Pacemaker IPaddr2 Resource Agent - ansible.builtin.shell: | - platform_cidr_32_static="{{ 'true' if (('amazon' in (ansible_system_vendor | lower) or 'amazon' in (ansible_product_name | lower)) or (ansible_product_name == 'Google Compute Engine')) }}" - if [ "$platform_cidr_32_static" = "true" ] && [ "{{ primary_ip_broadcast_address }}" = "" ] - then - ip address add {{ sap_vm_temp_vip_anydb_primary | regex_replace('/.*', '') }}/32 dev eth0 noprefixroute - elif [ "$platform_cidr_32_static" = "true" ] && [ "{{ primary_ip_broadcast_address }}" != "" ] - then - ip address add {{ sap_vm_temp_vip_anydb_primary | regex_replace('/.*', '') }}/32 brd {{ primary_ip_broadcast_address }} dev eth0 noprefixroute - elif [ "{{ primary_ip_broadcast_address }}" = "" ] && [ "{{ primary_ip_address_netmask_cidr_prefix }}" = "" ] - then - ip address add {{ sap_vm_temp_vip_anydb_primary | regex_replace('/.*', '') }}/32 brd + dev eth0 noprefixroute - elif [ "{{ primary_ip_broadcast_address }}" != "" ] && [ "{{ primary_ip_address_netmask_cidr_prefix }}" = "" ] && [ "{{ primary_ip_address_netmask }}" != "" ] - then - calculate_netmask_prefix=$(ipcalc --prefix 0.0.0.0 {{ primary_ip_address_netmask }}) - ip address add {{ sap_vm_temp_vip_anydb_primary | regex_replace('/.*', '') }}/$calculate_netmask_prefix brd {{ primary_ip_broadcast_address }} dev eth0 noprefixroute - elif [ "{{ primary_ip_broadcast_address }}" != "" ] && [ "{{ primary_ip_address_netmask_cidr_prefix }}" = "" ] && [ "{{ primary_ip_address_netmask }}" == "" ] && [ "{{ primary_ip_address }}" != "" ] - then - discover_network_adapter_active=$(ip route show default 0.0.0.0/0 | awk '/default/ {print $5}') - discover_primary_ip_cidr=$(ip -oneline address show $discover_network_adapter_active | grep {{ primary_ip_address }} | sed -n 's/.*inet \(.*\) brd.*/\1/p') - discover_netmask_prefix=$(basename $discover_primary_ip_cidr) - ip address add {{ sap_vm_temp_vip_anydb_primary | regex_replace('/.*', '') }}/$discover_netmask_prefix brd {{ primary_ip_broadcast_address }} dev eth0 noprefixroute - elif [ "{{ primary_ip_broadcast_address }}" = "" ] && "{{ primary_ip_address_netmask_cidr_prefix }}" != "" ] - then - discover_network_adapter_active=$(ip route show default 0.0.0.0/0 | awk '/default/ {print $5}') - discover_primary_ip_bridge=$(ip -oneline address show $discover_network_adapter_active | grep {{ primary_ip_address }} | sed -n 's/.*brd \(.*\) scope.*/\1/p') - ip address add {{ sap_vm_temp_vip_anydb_primary | regex_replace('/.*', '') }}/{{ primary_ip_address_netmask_cidr_prefix }} brd + dev eth0 noprefixroute - #ip address add {{ sap_vm_temp_vip_anydb_primary | regex_replace('/.*', '') }}/{{ primary_ip_address_netmask_cidr_prefix }} brd $discover_primary_ip_bridge dev eth0 noprefixroute - elif [ "{{ primary_ip_broadcast_address }}" != "" ] && [ "{{ primary_ip_address_netmask_cidr_prefix }}" != "" ] - then - ip address add {{ sap_vm_temp_vip_anydb_primary | regex_replace('/.*', '') }}/{{ primary_ip_address_netmask_cidr_prefix }} brd {{ primary_ip_broadcast_address }} dev eth0 noprefixroute - else - exit 1 - fi +# Dynamically generate IP creation command depending on values gathered before: +# 1. VIP address is defined based on target host group +# 2. Prefix is defined or generated using netmask or primary IP prefix +# 3. Broadcast IP is used only if it was defined or generated using primary IP broadcast +- name: Generate command for IP creation - Prefix /{{ __sap_vm_temp_vip_prefix }} static IPs + ansible.builtin.set_fact: + __sap_vm_temp_vip_command: >- + {%- if __sap_vm_temp_vip_broadcast | length > 0 -%} + ip address add {{ __sap_vm_temp_vip_address }}/{{ __sap_vm_temp_vip_prefix }} brd {{ __sap_vm_temp_vip_broadcast }} dev {{ sap_vm_temp_vip_default_interface }} noprefixroute + {%- else -%} + ip address add {{ __sap_vm_temp_vip_address }}/{{ __sap_vm_temp_vip_prefix }} brd + dev {{ sap_vm_temp_vip_default_interface }} noprefixroute + {%- endif -%} when: - - (groups["anydb_secondary"] is defined and inventory_hostname in groups["anydb_primary"]) and (groups["anydb_secondary"] is defined and (groups["anydb_secondary"]|length>0)) - - not ansible_chassis_asset_tag == 'ibmcloud' - ignore_errors: true # Ignore when stderr is 'RTNETLINK answers: File exists' + - __sap_vm_temp_vip_address is defined and __sap_vm_temp_vip_address | length > 0 + - __sap_vm_temp_vip_prefix | length > 0 + - __sap_vm_temp_vip_get_vip.stdout is defined and __sap_vm_temp_vip_get_vip.stdout | length == 0 -#### HA of ASCS/ERS #### - -# Required before running SAP SWPM -# Otherwise CSiManagerInterfaces.cpp will provide WARNING "The host with the name XXXXXX defined by SAPINST_USE_HOSTNAME is not a virtual host on the local host." -# And if the Virtual Hostname / Virtual IP cannot resolve, it will likely prevent SAP SWPM from completing the installation -- name: Append temporary Virtual IP (VIP) to network interface for SAP NetWeaver ASCS, will be replaced by Linux Pacemaker IPaddr2 Resource Agent - ansible.builtin.shell: | - platform_cidr_32_static="{{ 'true' if (('amazon' in (ansible_system_vendor | lower) or 'amazon' in (ansible_product_name | lower)) or (ansible_product_name == 'Google Compute Engine')) }}" - if [ "$platform_cidr_32_static" = "true" ] && [ "{{ primary_ip_broadcast_address }}" = "" ] - then - ip address add {{ sap_vm_temp_vip_nwas_abap_ascs | regex_replace('/.*', '') }}/32 dev eth0 noprefixroute - elif [ "$platform_cidr_32_static" = "true" ] && [ "{{ primary_ip_broadcast_address }}" != "" ] - then - ip address add {{ sap_vm_temp_vip_nwas_abap_ascs | regex_replace('/.*', '') }}/32 brd {{ primary_ip_broadcast_address }} dev eth0 noprefixroute - elif [ "{{ primary_ip_broadcast_address }}" = "" ] && [ "{{ primary_ip_address_netmask_cidr_prefix }}" = "" ] - then - ip address add {{ sap_vm_temp_vip_nwas_abap_ascs | regex_replace('/.*', '') }}/32 brd + dev eth0 noprefixroute - elif [ "{{ primary_ip_broadcast_address }}" != "" ] && [ "{{ primary_ip_address_netmask_cidr_prefix }}" = "" ] && [ "{{ primary_ip_address_netmask }}" != "" ] - then - calculate_netmask_prefix=$(ipcalc --prefix 0.0.0.0 {{ primary_ip_address_netmask }}) - ip address add {{ sap_vm_temp_vip_nwas_abap_ascs | regex_replace('/.*', '') }}/$calculate_netmask_prefix brd {{ primary_ip_broadcast_address }} dev eth0 noprefixroute - elif [ "{{ primary_ip_broadcast_address }}" != "" ] && [ "{{ primary_ip_address_netmask_cidr_prefix }}" = "" ] && [ "{{ primary_ip_address_netmask }}" == "" ] && [ "{{ primary_ip_address }}" != "" ] - then - discover_network_adapter_active=$(ip route show default 0.0.0.0/0 | awk '/default/ {print $5}') - discover_primary_ip_cidr=$(ip -oneline address show $discover_network_adapter_active | grep {{ primary_ip_address }} | sed -n 's/.*inet \(.*\) brd.*/\1/p') - discover_netmask_prefix=$(basename $discover_primary_ip_cidr) - ip address add {{ sap_vm_temp_vip_nwas_abap_ascs | regex_replace('/.*', '') }}/$discover_netmask_prefix brd {{ primary_ip_broadcast_address }} dev eth0 noprefixroute - elif [ "{{ primary_ip_broadcast_address }}" = "" ] && "{{ primary_ip_address_netmask_cidr_prefix }}" != "" ] - then - discover_network_adapter_active=$(ip route show default 0.0.0.0/0 | awk '/default/ {print $5}') - discover_primary_ip_bridge=$(ip -oneline address show $discover_network_adapter_active | grep {{ primary_ip_address }} | sed -n 's/.*brd \(.*\) scope.*/\1/p') - ip address add {{ sap_vm_temp_vip_nwas_abap_ascs | regex_replace('/.*', '') }}/{{ primary_ip_address_netmask_cidr_prefix }} brd + dev eth0 noprefixroute - #ip address add {{ sap_vm_temp_vip_nwas_abap_ascs | regex_replace('/.*', '') }}/{{ primary_ip_address_netmask_cidr_prefix }} brd $discover_primary_ip_bridge dev eth0 noprefixroute - elif [ "{{ primary_ip_broadcast_address }}" != "" ] && [ "{{ primary_ip_address_netmask_cidr_prefix }}" != "" ] - then - ip address add {{ sap_vm_temp_vip_nwas_abap_ascs | regex_replace('/.*', '') }}/{{ primary_ip_address_netmask_cidr_prefix }} brd {{ primary_ip_broadcast_address }} dev eth0 noprefixroute - else - exit 1 - fi +# Show debug information with input details if command was generated: +- name: Show actions to be executed to create temporary VIP + ansible.builtin.debug: + msg: + - "Ansible Facts:" + - primary_ip_address = {{ sap_vm_temp_vip_default_ip }} + - primary_ip_address_netmask = {{ sap_vm_temp_vip_default_netmask }} + - primary_ip_address_netmask_cidr_prefix = {{ __sap_vm_temp_vip_prefix }} + - primary_ip_broadcast_address = {{ __sap_vm_temp_vip_broadcast }} + - "" + - "Command to be executed:" + - "{{ __sap_vm_temp_vip_command }}" when: - - (groups["nwas_ers"] is defined and inventory_hostname in groups["nwas_ascs"]) and (groups["nwas_ers"] is defined and (groups["nwas_ers"]|length>0)) - - not ansible_chassis_asset_tag == 'ibmcloud' - ignore_errors: true # Ignore when stderr is 'RTNETLINK answers: File exists' + - __sap_vm_temp_vip_command is defined and __sap_vm_temp_vip_command | length > 0 + - __sap_vm_temp_vip_get_vip.stdout is defined and __sap_vm_temp_vip_get_vip.stdout | length == 0 -# Required before running SAP SWPM -# Otherwise CSiManagerInterfaces.cpp will provide WARNING "The host with the name XXXXXX defined by SAPINST_USE_HOSTNAME is not a virtual host on the local host." -# And if the Virtual Hostname / Virtual IP cannot resolve, it will likely prevent SAP SWPM from completing the installation -- name: Append temporary Virtual IP (VIP) to network interface for SAP NetWeaver ERS, will be replaced by Linux Pacemaker IPaddr2 Resource Agent - ansible.builtin.shell: | - platform_cidr_32_static="{{ 'true' if (('amazon' in (ansible_system_vendor | lower) or 'amazon' in (ansible_product_name | lower)) or (ansible_product_name == 'Google Compute Engine')) }}" - if [ "$platform_cidr_32_static" = "true" ] && [ "{{ primary_ip_broadcast_address }}" = "" ] - then - ip address add {{ sap_vm_temp_vip_nwas_abap_ers | regex_replace('/.*', '') }}/32 dev eth0 noprefixroute - elif [ "$platform_cidr_32_static" = "true" ] && [ "{{ primary_ip_broadcast_address }}" != "" ] - then - ip address add {{ sap_vm_temp_vip_nwas_abap_ers | regex_replace('/.*', '') }}/32 brd {{ primary_ip_broadcast_address }} dev eth0 noprefixroute - elif [ "{{ primary_ip_broadcast_address }}" = "" ] && [ "{{ primary_ip_address_netmask_cidr_prefix }}" = "" ] - then - ip address add {{ sap_vm_temp_vip_nwas_abap_ers | regex_replace('/.*', '') }}/32 brd + dev eth0 noprefixroute - elif [ "{{ primary_ip_broadcast_address }}" != "" ] && [ "{{ primary_ip_address_netmask_cidr_prefix }}" = "" ] && [ "{{ primary_ip_address_netmask }}" != "" ] - then - calculate_netmask_prefix=$(ipcalc --prefix 0.0.0.0 {{ primary_ip_address_netmask }}) - ip address add {{ sap_vm_temp_vip_nwas_abap_ers | regex_replace('/.*', '') }}/$calculate_netmask_prefix brd {{ primary_ip_broadcast_address }} dev eth0 noprefixroute - elif [ "{{ primary_ip_broadcast_address }}" != "" ] && [ "{{ primary_ip_address_netmask_cidr_prefix }}" = "" ] && [ "{{ primary_ip_address_netmask }}" == "" ] && [ "{{ primary_ip_address }}" != "" ] - then - discover_network_adapter_active=$(ip route show default 0.0.0.0/0 | awk '/default/ {print $5}') - discover_primary_ip_cidr=$(ip -oneline address show $discover_network_adapter_active | grep {{ primary_ip_address }} | sed -n 's/.*inet \(.*\) brd.*/\1/p') - discover_netmask_prefix=$(basename $discover_primary_ip_cidr) - ip address add {{ sap_vm_temp_vip_nwas_abap_ers | regex_replace('/.*', '') }}/$discover_netmask_prefix brd {{ primary_ip_broadcast_address }} dev eth0 noprefixroute - elif [ "{{ primary_ip_broadcast_address }}" = "" ] && "{{ primary_ip_address_netmask_cidr_prefix }}" != "" ] - then - discover_network_adapter_active=$(ip route show default 0.0.0.0/0 | awk '/default/ {print $5}') - discover_primary_ip_bridge=$(ip -oneline address show $discover_network_adapter_active | grep {{ primary_ip_address }} | sed -n 's/.*brd \(.*\) scope.*/\1/p') - ip address add {{ sap_vm_temp_vip_nwas_abap_ers | regex_replace('/.*', '') }}/{{ primary_ip_address_netmask_cidr_prefix }} brd + dev eth0 noprefixroute - #ip address add {{ sap_vm_temp_vip_nwas_abap_ers | regex_replace('/.*', '') }}/{{ primary_ip_address_netmask_cidr_prefix }} brd $discover_primary_ip_bridge dev eth0 noprefixroute - elif [ "{{ primary_ip_broadcast_address }}" != "" ] && [ "{{ primary_ip_address_netmask_cidr_prefix }}" != "" ] - then - ip address add {{ sap_vm_temp_vip_nwas_abap_ers | regex_replace('/.*', '') }}/{{ primary_ip_address_netmask_cidr_prefix }} brd {{ primary_ip_broadcast_address }} dev eth0 noprefixroute - else - exit 1 - fi +# Show debug information with input details if command was not generated and some inputs are empty: +- name: Show information if command was unable to be generated + ansible.builtin.debug: + msg: + - "ERROR: Unable to generate command because of lacking data." + - "" + - "Please review facts below, to see which are empty or missing:" + - primary_ip_address = {{ sap_vm_temp_vip_default_ip }} + - primary_ip_address_netmask = {{ sap_vm_temp_vip_default_netmask }} + - primary_ip_address_netmask_cidr_prefix = {{ __sap_vm_temp_vip_prefix }} + - primary_ip_broadcast_address = {{ __sap_vm_temp_vip_broadcast }} when: - - (groups["nwas_ers"] is defined and inventory_hostname in groups["nwas_ers"]) and (groups["nwas_ers"] is defined and (groups["nwas_ers"]|length>0)) - - not ansible_chassis_asset_tag == 'ibmcloud' - ignore_errors: true # Ignore when stderr is 'RTNETLINK answers: File exists' - + - __sap_vm_temp_vip_command is not defined or (__sap_vm_temp_vip_command is defined and __sap_vm_temp_vip_command | length == 0) + - __sap_vm_temp_vip_get_vip.stdout is defined and __sap_vm_temp_vip_get_vip.stdout | length == 0 -#### HA of PAS/AAS [rare, comment out] #### - -# # Required before running SAP SWPM -# # Otherwise CSiManagerInterfaces.cpp will provide WARNING "The host with the name XXXXXX defined by SAPINST_USE_HOSTNAME is not a virtual host on the local host." -# # And if the Virtual Hostname / Virtual IP cannot resolve, it will likely prevent SAP SWPM from completing the installation -# - name: Append temporary Virtual IP (VIP) to network interface for SAP NetWeaver PAS, will be replaced by Linux Pacemaker IPaddr2 Resource Agent -# ansible.builtin.shell: -# when: -# - (groups["nwas_pas"] is defined and inventory_hostname in groups["nwas_pas"]) and (groups["nwas_pas"] is defined and (groups["nwas_pas"]|length>0)) -# - not ansible_chassis_asset_tag == 'ibmcloud' -# ignore_errors: true - -# # Required before running SAP SWPM -# # Otherwise CSiManagerInterfaces.cpp will provide WARNING "The host with the name XXXXXX defined by SAPINST_USE_HOSTNAME is not a virtual host on the local host." -# # And if the Virtual Hostname / Virtual IP cannot resolve, it will likely prevent SAP SWPM from completing the installation -# - name: Append temporary Virtual IP (VIP) to network interface for SAP NetWeaver AAS, will be replaced by Linux Pacemaker IPaddr2 Resource Agent -# ansible.builtin.shell: -# when: -# - (groups["nwas_pas"] is defined and inventory_hostname in groups["nwas_aas"]) and (groups["nwas_pas"] is defined and (groups["nwas_pas"]|length>0)) -# - not ansible_chassis_asset_tag == 'ibmcloud' -# ignore_errors: true +# Execute generated command to add VIP to network interface if command is generated and VIP is not present yet. +- name: Append temporary Virtual IP (VIP) to network interface # noqa command-instead-of-shell + ansible.builtin.shell: + cmd: "{{ __sap_vm_temp_vip_command }}" + when: + - __sap_vm_temp_vip_command is defined and __sap_vm_temp_vip_command | length > 0 + - __sap_vm_temp_vip_get_vip.stdout | length == 0 + register: __sap_vm_temp_vip_command_output diff --git a/roles/sap_vm_temp_vip/tasks/set_temp_vip_lb_listener.yml b/roles/sap_vm_temp_vip/tasks/set_temp_vip_lb_listener.yml index 79232a6..3703a40 100644 --- a/roles/sap_vm_temp_vip/tasks/set_temp_vip_lb_listener.yml +++ b/roles/sap_vm_temp_vip/tasks/set_temp_vip_lb_listener.yml @@ -1,48 +1,63 @@ --- +# Define listening port based on target host group. Same ports are used by sap_vm_provision during NLB creation +# 55550 - SAP HANA and SAP AnyDB +# 55551 - SAP SAP NetWeaver ASCS +# 55552 - SAP NetWeaver ERS +- name: Set fact for temporary listening port + ansible.builtin.set_fact: + __sap_vm_temp_vip_port: >- + {% if groups['hana_secondary'] | d([]) | length > 0 and inventory_hostname in groups["hana_primary"] -%} + 55550 + {%- elif groups['anydb_secondary'] | d([]) | length > 0 and inventory_hostname in groups["anydb_primary"] -%} + 55550 + {%- elif groups["nwas_ers"] | d([]) | length > 0 and inventory_hostname in groups["nwas_ascs"] -%} + 55551 + {%- elif groups["nwas_ers"] | d([]) | length > 0 and inventory_hostname in groups["nwas_ers"] -%} + 55552 + {%- endif %} -- name: Install netcat and lsof utils - ansible.builtin.package: - name: - - "{{ 'netcat' if ansible_os_family == 'Suse' else 'nc' }}" - - lsof - state: present - - -# Must use while loop to avoid netcat process ending too early -# Required when using Load Balancers (i.e. Google Cloud, IBM Cloud, MS Azure) -# Temporary listener for SAP HANA or SAP AnyDB used is 55550 -# Temporary listener for SAP NetWeaver ASCS used is 55551 -# Temporary listener for SAP NetWeaver ERS used is 55552; must be different to ASCS Health Check Port to avoid ASCS VIP distributing to ERS host - -- name: Start netcat temporary listen on port 55550 for SAP HANA or SAP AnyDB for 12 hours (until SAP installation complete) to respond to Load Balancer Health Check probe until Linux Pacemaker started - ansible.builtin.shell: | - if ! $(lsof -Pi :55550 -sTCP:LISTEN -t >/dev/null) ; then - nohup timeout 12h bash -c "while true; do nc -vvv -l -k 55550 ; done" /dev/null 2>&1 & - sleep 2 - fi - when: - - (groups["hana_secondary"] is defined and (groups["hana_secondary"]|length>0)) or (groups["anydb_secondary"] is defined and (groups["anydb_secondary"]|length>0)) - - (groups["hana_secondary"] is defined and inventory_hostname in groups["hana_primary"]) or (groups["anydb_secondary"] is defined and inventory_hostname in groups["anydb_primary"]) - - (ansible_product_name == 'Google Compute Engine') or (ansible_chassis_asset_tag == 'ibmcloud') or (ansible_chassis_vendor == 'Microsoft Corporation' and ansible_product_name == 'Virtual Machine') - -- name: Start netcat temporary listen on port 55551 for SAP NetWeaver ASCS for 12 hours (until SAP installation complete) to respond to Load Balancer Health Check probe until Linux Pacemaker started - ansible.builtin.shell: | - if ! $(lsof -Pi :55551 -sTCP:LISTEN -t >/dev/null) ; then - nohup timeout 12h bash -c "while true; do nc -vvv -l -k 55551 ; done" /dev/null 2>&1 & - sleep 2 - fi + +# Check if defined port is alreadu active and listening +# ss is used as it is present on both SUSE and Red Hat OS without need to install lsof or netstat +- name: Check if temporary port is already open + ansible.builtin.command: + cmd: ss -tulnH "sport = :{{ __sap_vm_temp_vip_port }}" + register: __sap_vm_temp_vip_port_check when: - - (groups["nwas_ers"] is defined and (groups["nwas_ers"]|length>0)) - - (groups["nwas_ers"] is defined and inventory_hostname in groups["nwas_ascs"]) - - (ansible_product_name == 'Google Compute Engine') or (ansible_chassis_asset_tag == 'ibmcloud') or (ansible_chassis_vendor == 'Microsoft Corporation' and ansible_product_name == 'Virtual Machine') - -- name: Start netcat temporary listen on port 55552 for SAP NetWeaver ERS for 12 hours (until SAP installation complete) to respond to Load Balancer Health Check probe until Linux Pacemaker started - ansible.builtin.shell: | - if ! $(lsof -Pi :55552 -sTCP:LISTEN -t >/dev/null) ; then - nohup timeout 12h bash -c "while true; do nc -vvv -l -k 55552 ; done" /dev/null 2>&1 & - sleep 2 - fi + - __sap_vm_temp_vip_port is defined and __sap_vm_temp_vip_port | length > 0 + changed_when: false + + +- name: Block to start temporary netcat processes for Load Balancer Health Checks when: - - (groups["nwas_ers"] is defined and (groups["nwas_ers"]|length>0)) - - (groups["nwas_ers"] is defined and inventory_hostname in groups["nwas_ers"]) - - (ansible_product_name == 'Google Compute Engine') or (ansible_chassis_asset_tag == 'ibmcloud') or (ansible_chassis_vendor == 'Microsoft Corporation' and ansible_product_name == 'Virtual Machine') + - __sap_vm_temp_vip_address is defined and __sap_vm_temp_vip_address | length > 0 + - __sap_vm_temp_vip_port is defined and __sap_vm_temp_vip_port | length > 0 + - __sap_vm_temp_vip_port_check.stdout is defined and __sap_vm_temp_vip_port_check.stdout | length == 0 + - __sap_vm_temp_vip_prefix | length > 0 # Dont execute if prefix was empty during VIP creation + block: + + # Get content of ip address show filtered by VIP - Additional execution if VIP was previously created + - name: Check if VIP is was already attached to network interface + ansible.builtin.shell: + cmd: "set -o pipefail && ip --oneline address show | grep {{ __sap_vm_temp_vip_address }}/{{ __sap_vm_temp_vip_prefix }}" + executable: /bin/bash + register: __sap_vm_temp_vip_check_ip + changed_when: false + ignore_errors: true + failed_when: false + + # Install netcat package that is used to temporarily listen on ports + - name: Install netcat package + ansible.builtin.package: + name: + - "{{ 'netcat' if ansible_os_family == 'Suse' else 'nc' }}" + state: present + when: __sap_vm_temp_vip_check_ip.stdout | length > 0 + + + # Start netcat with timeout of 12 hours to ensure that SAP installation has enough time to complete. + - name: Start temporary 12 hour netcat process for Load Balancer Health Checks during SAP installation + ansible.builtin.shell: | + nohup timeout 12h bash -c "while true; do nc -vvv -l -k {{ __sap_vm_temp_vip_port }} ; done" /dev/null 2>&1 & + sleep 2 + when: __sap_vm_temp_vip_check_ip.stdout | length > 0