From 278602fbdccceddd8d940ca7b78500c4bf4a9fc6 Mon Sep 17 00:00:00 2001 From: Marcel Mamula Date: Mon, 23 Sep 2024 10:54:10 +0200 Subject: [PATCH 1/8] feat: Replaced shell with ansible tasks Dynamic execution based on groups Calculation of missing inputs Idempotency checks before execution --- roles/sap_vm_temp_vip/INPUT_PARAMETERS.md | 58 ++++ roles/sap_vm_temp_vip/README.md | 133 ++++--- roles/sap_vm_temp_vip/defaults/main.yml | 7 + .../tasks/get_temp_vip_details.yml | 100 ++++++ roles/sap_vm_temp_vip/tasks/main.yml | 26 +- roles/sap_vm_temp_vip/tasks/set_temp_vip.yml | 325 ++++++------------ .../tasks/set_temp_vip_lb_listener.yml | 103 +++--- 7 files changed, 413 insertions(+), 339 deletions(-) create mode 100644 roles/sap_vm_temp_vip/INPUT_PARAMETERS.md create mode 100644 roles/sap_vm_temp_vip/tasks/get_temp_vip_details.yml 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..7b82d43 --- /dev/null +++ b/roles/sap_vm_temp_vip/INPUT_PARAMETERS.md @@ -0,0 +1,58 @@ +## Input Parameters for sap_vm_temp_vip Ansible Role + +### sap_vm_temp_vip_primary_ip + +- _Type:_ `string` +- _Default:_ `ansible_default_ipv4.address` + +Primary IP on default network interface is obtained from Ansible Facts and it is used for calculation of missing input parameters. + +### sap_vm_temp_vip_primary_netmask + +- _Type:_ `string` +- _Default:_ `ansible_default_ipv4.netmask` + +Netmask of primary IP on default network interface is obtained from Ansible Facts and it is used for calculation of missing input parameters. + +### sap_vm_temp_vip_primary_prefix + +- _Type:_ `string` +- _Default:_ `ansible_default_ipv4.prefix` + +Prefix of primary IP on default network interface is obtained from Ansible Facts and it is used for calculation of missing input parameters. + +### sap_vm_temp_vip_primary_broadcast + +- _Type:_ `string` +- _Default:_ `ansible_default_ipv4.broadcast` + +Broadcast of primary IP on 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_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..32dc7ac 100644 --- a/roles/sap_vm_temp_vip/README.md +++ b/roles/sap_vm_temp_vip/README.md @@ -1,81 +1,78 @@ -`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, because that is completed by [sap_vm_provision](https://github.com/sap-linuxlab/community.sap_infrastructure/tree/main/roles/sap_vm_provision) role. + +## Prerequisites + +Environment: +1. Execute role with `gather_facts: true`, as it uses `ansible_default_ipv4` facts. +2. 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 - +Dependant on roles: +1. [sap_vm_provision](https://github.com/sap-linuxlab/community.sap_infrastructure/tree/main/roles/sap_vm_provision) to create dependant 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..f928a11 100644 --- a/roles/sap_vm_temp_vip/defaults/main.yml +++ b/roles/sap_vm_temp_vip/defaults/main.yml @@ -1,5 +1,12 @@ --- +# General variables that are calculated from Ansible facts +sap_vm_temp_vip_primary_ip: "{{ ansible_default_ipv4.address | default('') }}" +sap_vm_temp_vip_primary_netmask: "{{ ansible_default_ipv4.netmask | default('') }}" +sap_vm_temp_vip_primary_prefix: "{{ ansible_default_ipv4.prefix | default('') }}" +sap_vm_temp_vip_primary_broadcast: "{{ ansible_default_ipv4.broadcast | default('') }}" + +# 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..e18104d --- /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_primary_ip }} + ansible.builtin.shell: + cmd: set -o pipefail && ip -oneline address show {{ __sap_vm_temp_vip_get_route.stdout }} | grep {{ sap_vm_temp_vip_primary_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_primary_netmask }} + delegate_to: localhost + ansible.builtin.command: + cmd: > + python3 -c "import ipaddress; print(ipaddress.IPv4Network('{{ sap_vm_temp_vip_primary_ip }}/{{ sap_vm_temp_vip_primary_netmask }}', strict=False).prefixlen)" + when: + - sap_vm_temp_vip_primary_prefix == '' + - sap_vm_temp_vip_primary_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_primary_ip }} if sap_vm_temp_vip_primary_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_primary_prefix == '' + - sap_vm_temp_vip_primary_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. Use /32 for AWS and GCP if sap_vm_temp_vip_primary_prefix is empty +# 2. Else use prefix calculated from netmask if it is available and sap_vm_temp_vip_primary_prefix is empty +# 3. Else use prefix calculated from primary IP if netmask is not available and sap_vm_temp_vip_primary_prefix is empty +# 4. Else use sap_vm_temp_vip_primary_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_primary_prefix: >- + {%- if sap_vm_temp_vip_primary_prefix | length == 0 and __sap_vm_temp_vip_force_static_32 -%} + 32 + {%- elif sap_vm_temp_vip_primary_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_primary_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_primary_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_primary_ip }} if sap_vm_temp_vip_primary_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_primary_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_primary_broadcast is empty +# 2. Else use sap_vm_temp_vip_primary_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_primary_broadcast: >- + {%- if sap_vm_temp_vip_primary_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_primary_broadcast }} + {%- endif -%} diff --git a/roles/sap_vm_temp_vip/tasks/main.yml b/roles/sap_vm_temp_vip/tasks/main.yml index cdcd579..7243a20 100644 --- a/roles/sap_vm_temp_vip/tasks/main.yml +++ b/roles/sap_vm_temp_vip/tasks/main.yml @@ -1,14 +1,36 @@ --- +# Ansible role to setup temporary Virtual IP (VIP) -- name: Setup temporary Virtual IP (VIP) +- name: Assert that sap_vm_temp_vip_primary_ip is defined + ansible.builtin.assert: + that: sap_vm_temp_vip_primary_ip is defined and sap_vm_temp_vip_primary_ip | length > 0 + fail_msg: + - "Unable to get ansible fact ansible_default_ipv4.address or variable sap_vm_temp_vip_primary_ip is empty!" + - "Ensure that gather_facts:true is set and sap_vm_temp_vip_primary_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..a2c5adb 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_primary_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_primary_prefix. + {%- endif -%} + vars: + __vip_expected: "{{ __sap_vm_temp_vip_address ~ '/' ~ __sap_vm_temp_vip_primary_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_primary_prefix }} static IPs + ansible.builtin.set_fact: + __sap_vm_temp_vip_command: >- + {%- if __sap_vm_temp_vip_primary_broadcast | length > 0 -%} + ip address add {{ __sap_vm_temp_vip_address }}/{{ __sap_vm_temp_vip_primary_prefix }} brd {{ __sap_vm_temp_vip_primary_broadcast }} dev eth0 noprefixroute + {%- else -%} + ip address add {{ __sap_vm_temp_vip_address }}/{{ __sap_vm_temp_vip_primary_prefix }} brd + dev eth0 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_primary_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_primary_ip }} + - primary_ip_address_netmask = {{ sap_vm_temp_vip_primary_netmask }} + - primary_ip_address_netmask_cidr_prefix = {{ __sap_vm_temp_vip_primary_prefix }} + - primary_ip_broadcast_address = {{ __sap_vm_temp_vip_primary_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_primary_ip }} + - primary_ip_address_netmask = {{ sap_vm_temp_vip_primary_netmask }} + - primary_ip_address_netmask_cidr_prefix = {{ __sap_vm_temp_vip_primary_prefix }} + - primary_ip_broadcast_address = {{ __sap_vm_temp_vip_primary_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..3db532c 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_primary_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_primary_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 From a24ff8d87b560a3565033c537b91cb4134ff3e64 Mon Sep 17 00:00:00 2001 From: Marcel Mamula Date: Mon, 23 Sep 2024 11:05:02 +0200 Subject: [PATCH 2/8] fix: dependent typos --- roles/sap_vm_temp_vip/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/roles/sap_vm_temp_vip/README.md b/roles/sap_vm_temp_vip/README.md index 32dc7ac..8a411e3 100644 --- a/roles/sap_vm_temp_vip/README.md +++ b/roles/sap_vm_temp_vip/README.md @@ -19,8 +19,8 @@ Environment: 2. 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` -Dependant on roles: -1. [sap_vm_provision](https://github.com/sap-linuxlab/community.sap_infrastructure/tree/main/roles/sap_vm_provision) to create dependant resources: DNS, Load Balancers and Health Checks. +Dependent on roles: +1. [sap_vm_provision](https://github.com/sap-linuxlab/community.sap_infrastructure/tree/main/roles/sap_vm_provision) to create required resources: DNS, Load Balancers and Health Checks. ## Execution From f01c5e9bea04000e503b907da5ea5377935d9791 Mon Sep 17 00:00:00 2001 From: Marcel Mamula Date: Mon, 23 Sep 2024 14:11:46 +0200 Subject: [PATCH 3/8] fix: correction of readme --- roles/sap_vm_temp_vip/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/roles/sap_vm_temp_vip/README.md b/roles/sap_vm_temp_vip/README.md index 8a411e3..9e51e0a 100644 --- a/roles/sap_vm_temp_vip/README.md +++ b/roles/sap_vm_temp_vip/README.md @@ -9,7 +9,7 @@ Ansible role `sap_vm_temp_vip` is used to enable installation of SAP Application 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. -This role does not update `/etc/hosts` or DNS, because that is completed by [sap_vm_provision](https://github.com/sap-linuxlab/community.sap_infrastructure/tree/main/roles/sap_vm_provision) role. +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 @@ -19,8 +19,8 @@ Environment: 2. 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` -Dependent on roles: -1. [sap_vm_provision](https://github.com/sap-linuxlab/community.sap_infrastructure/tree/main/roles/sap_vm_provision) to create required resources: DNS, Load Balancers and Health Checks. +Role dependency: +1. [sap_vm_provision](https://github.com/sap-linuxlab/community.sap_infrastructure/tree/main/roles/sap_vm_provision), for creating the following required resources: DNS, Load Balancers and Health Checks. ## Execution From dc0121d6847d84e9936d83f46378c18888c9d1d7 Mon Sep 17 00:00:00 2001 From: Marcel Mamula Date: Tue, 24 Sep 2024 12:12:43 +0200 Subject: [PATCH 4/8] fix: Typo in markdown block --- roles/sap_vm_temp_vip/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/sap_vm_temp_vip/README.md b/roles/sap_vm_temp_vip/README.md index 9e51e0a..063a434 100644 --- a/roles/sap_vm_temp_vip/README.md +++ b/roles/sap_vm_temp_vip/README.md @@ -60,7 +60,7 @@ Role can be execute separately or as part of [ansible.playbooks_for_sap](https:/ - ## License From c2699a935cd6fe4d3ef14933cf05e5560751ec7e Mon Sep 17 00:00:00 2001 From: Marcel Mamula Date: Wed, 25 Sep 2024 09:39:15 +0200 Subject: [PATCH 5/8] fix: default 32 for AWS and GCP --- roles/sap_vm_temp_vip/tasks/get_temp_vip_details.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 index e18104d..3532246 100644 --- a/roles/sap_vm_temp_vip/tasks/get_temp_vip_details.yml +++ b/roles/sap_vm_temp_vip/tasks/get_temp_vip_details.yml @@ -48,14 +48,14 @@ # Combine final prefix variable based on decision below: -# 1. Use /32 for AWS and GCP if sap_vm_temp_vip_primary_prefix is empty +# 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_primary_prefix is empty # 3. Else use prefix calculated from primary IP if netmask is not available and sap_vm_temp_vip_primary_prefix is empty # 4. Else use sap_vm_temp_vip_primary_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_primary_prefix: >- - {%- if sap_vm_temp_vip_primary_prefix | length == 0 and __sap_vm_temp_vip_force_static_32 -%} + {%- if __sap_vm_temp_vip_force_static_32 -%} 32 {%- elif sap_vm_temp_vip_primary_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 -%} From ee0d67a47fd6e48066b3878eecaf4644c6d351bd Mon Sep 17 00:00:00 2001 From: Marcel Mamula Date: Thu, 26 Sep 2024 09:36:01 +0200 Subject: [PATCH 6/8] Update variable names for simpler understanding --- roles/sap_vm_temp_vip/INPUT_PARAMETERS.md | 24 ++++++---- roles/sap_vm_temp_vip/defaults/main.yml | 9 ++-- .../tasks/get_temp_vip_details.yml | 46 +++++++++---------- roles/sap_vm_temp_vip/tasks/main.yml | 8 ++-- roles/sap_vm_temp_vip/tasks/set_temp_vip.yml | 32 ++++++------- .../tasks/set_temp_vip_lb_listener.yml | 4 +- 6 files changed, 66 insertions(+), 57 deletions(-) diff --git a/roles/sap_vm_temp_vip/INPUT_PARAMETERS.md b/roles/sap_vm_temp_vip/INPUT_PARAMETERS.md index 7b82d43..6ef4192 100644 --- a/roles/sap_vm_temp_vip/INPUT_PARAMETERS.md +++ b/roles/sap_vm_temp_vip/INPUT_PARAMETERS.md @@ -1,34 +1,42 @@ ## Input Parameters for sap_vm_temp_vip Ansible Role -### sap_vm_temp_vip_primary_ip +### sap_vm_temp_vip_default_ip - _Type:_ `string` - _Default:_ `ansible_default_ipv4.address` -Primary IP on default network interface is obtained from Ansible Facts and it is used for calculation of missing input parameters. +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_primary_netmask +### sap_vm_temp_vip_default_netmask - _Type:_ `string` - _Default:_ `ansible_default_ipv4.netmask` -Netmask of primary IP on default network interface is obtained from Ansible Facts and it is used for calculation of missing input parameters. +Netmask of default network interface is obtained from Ansible Facts and it is used for calculation of missing input parameters. -### sap_vm_temp_vip_primary_prefix +### sap_vm_temp_vip_default_prefix - _Type:_ `string` - _Default:_ `ansible_default_ipv4.prefix` -Prefix of primary IP on default network interface is obtained from Ansible Facts and it is used for calculation of missing input parameters. +Prefix of default network interface is obtained from Ansible Facts and it is used for calculation of missing input parameters. -### sap_vm_temp_vip_primary_broadcast +### sap_vm_temp_vip_default_broadcast - _Type:_ `string` - _Default:_ `ansible_default_ipv4.broadcast` -Broadcast of primary IP on default network interface is obtained from Ansible Facts and it is used for calculation of missing input parameters.
+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` diff --git a/roles/sap_vm_temp_vip/defaults/main.yml b/roles/sap_vm_temp_vip/defaults/main.yml index f928a11..df7e520 100644 --- a/roles/sap_vm_temp_vip/defaults/main.yml +++ b/roles/sap_vm_temp_vip/defaults/main.yml @@ -1,9 +1,10 @@ --- # General variables that are calculated from Ansible facts -sap_vm_temp_vip_primary_ip: "{{ ansible_default_ipv4.address | default('') }}" -sap_vm_temp_vip_primary_netmask: "{{ ansible_default_ipv4.netmask | default('') }}" -sap_vm_temp_vip_primary_prefix: "{{ ansible_default_ipv4.prefix | default('') }}" -sap_vm_temp_vip_primary_broadcast: "{{ ansible_default_ipv4.broadcast | default('') }}" +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 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 index 3532246..1ed03ab 100644 --- a/roles/sap_vm_temp_vip/tasks/get_temp_vip_details.yml +++ b/roles/sap_vm_temp_vip/tasks/get_temp_vip_details.yml @@ -8,9 +8,9 @@ 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_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_primary_ip }} + 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 @@ -19,14 +19,14 @@ # 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_primary_netmask }} +- 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_primary_ip }}/{{ sap_vm_temp_vip_primary_netmask }}', strict=False).prefixlen)" + 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_primary_prefix == '' - - sap_vm_temp_vip_primary_netmask | length > 0 + - 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 @@ -34,37 +34,37 @@ # 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_primary_ip }} if sap_vm_temp_vip_primary_netmask is empty +- 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_primary_prefix == '' - - sap_vm_temp_vip_primary_netmask == '' + - 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_primary_prefix is empty -# 3. Else use prefix calculated from primary IP if netmask is not available and sap_vm_temp_vip_primary_prefix is empty -# 4. Else use sap_vm_temp_vip_primary_prefix (regardless of content) to be used to skip steps. +# 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_primary_prefix: >- + __sap_vm_temp_vip_prefix: >- {%- if __sap_vm_temp_vip_force_static_32 -%} 32 - {%- elif sap_vm_temp_vip_primary_prefix | length == 0 + {%- 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_primary_prefix | length == 0 + {%- 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_primary_prefix }} + {{ sap_vm_temp_vip_default_prefix }} {%- endif -%} vars: __sap_vm_temp_vip_force_static_32: @@ -75,26 +75,26 @@ # 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_primary_ip }} if sap_vm_temp_vip_primary_broadcast is empty +- 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_primary_broadcast == '' + - 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_primary_broadcast is empty -# 2. Else use sap_vm_temp_vip_primary_broadcast (regardless of content) to be used during VIP creation +# 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_primary_broadcast: >- - {%- if sap_vm_temp_vip_primary_broadcast | length == 0 + __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_primary_broadcast }} + {{ 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 7243a20..ef5c775 100644 --- a/roles/sap_vm_temp_vip/tasks/main.yml +++ b/roles/sap_vm_temp_vip/tasks/main.yml @@ -1,12 +1,12 @@ --- # Ansible role to setup temporary Virtual IP (VIP) -- name: Assert that sap_vm_temp_vip_primary_ip is defined +- name: Assert that sap_vm_temp_vip_default_ip is defined ansible.builtin.assert: - that: sap_vm_temp_vip_primary_ip is defined and sap_vm_temp_vip_primary_ip | length > 0 + 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_primary_ip is empty!" - - "Ensure that gather_facts:true is set and sap_vm_temp_vip_primary_ip is not empty." + - "Unable to get ansible fact ansible_default_ipv4.address or variable sap_vm_temp_vip_default_ip is empty!" + - "Ensure that gather_facts:true is set and sap_vm_temp_vip_default_ip is not empty." - name: Block to ensure that only supported groups are allowed 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 a2c5adb..546b800 100644 --- a/roles/sap_vm_temp_vip/tasks/set_temp_vip.yml +++ b/roles/sap_vm_temp_vip/tasks/set_temp_vip.yml @@ -58,15 +58,15 @@ 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_primary_prefix != '' and not __vip_multiple -%} + {%- 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_primary_prefix. + 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_primary_prefix }}" + __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: @@ -78,17 +78,17 @@ # 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_primary_prefix }} static IPs +- 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_primary_broadcast | length > 0 -%} - ip address add {{ __sap_vm_temp_vip_address }}/{{ __sap_vm_temp_vip_primary_prefix }} brd {{ __sap_vm_temp_vip_primary_broadcast }} dev eth0 noprefixroute + {%- 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_primary_prefix }} brd + dev eth0 noprefixroute + ip address add {{ __sap_vm_temp_vip_address }}/{{ __sap_vm_temp_vip_prefix }} brd + dev {{ sap_vm_temp_vip_default_interface }} noprefixroute {%- endif -%} when: - __sap_vm_temp_vip_address is defined and __sap_vm_temp_vip_address | length > 0 - - __sap_vm_temp_vip_primary_prefix | 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 @@ -97,10 +97,10 @@ ansible.builtin.debug: msg: - "Ansible Facts:" - - primary_ip_address = {{ sap_vm_temp_vip_primary_ip }} - - primary_ip_address_netmask = {{ sap_vm_temp_vip_primary_netmask }} - - primary_ip_address_netmask_cidr_prefix = {{ __sap_vm_temp_vip_primary_prefix }} - - primary_ip_broadcast_address = {{ __sap_vm_temp_vip_primary_broadcast }} + - 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 }}" @@ -115,10 +115,10 @@ - "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_primary_ip }} - - primary_ip_address_netmask = {{ sap_vm_temp_vip_primary_netmask }} - - primary_ip_address_netmask_cidr_prefix = {{ __sap_vm_temp_vip_primary_prefix }} - - primary_ip_broadcast_address = {{ __sap_vm_temp_vip_primary_broadcast }} + - 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: - __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 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 3db532c..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 @@ -33,13 +33,13 @@ - __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_primary_prefix | length > 0 # Dont execute if prefix was empty during VIP creation + - __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_primary_prefix }}" + 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 From fa058db535650921aa44549eab238e6e81813a41 Mon Sep 17 00:00:00 2001 From: Marcel Mamula Date: Thu, 26 Sep 2024 10:09:25 +0200 Subject: [PATCH 7/8] Remove prereq and add setup task to get facts instead --- roles/sap_vm_temp_vip/README.md | 7 +++---- roles/sap_vm_temp_vip/tasks/main.yml | 6 ++++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/roles/sap_vm_temp_vip/README.md b/roles/sap_vm_temp_vip/README.md index 063a434..bd7e40c 100644 --- a/roles/sap_vm_temp_vip/README.md +++ b/roles/sap_vm_temp_vip/README.md @@ -15,12 +15,11 @@ This role does not update `/etc/hosts` or DNS records, as these steps are perfor ## Prerequisites Environment: -1. Execute role with `gather_facts: true`, as it uses `ansible_default_ipv4` facts. -2. 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` +- 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` Role dependency: -1. [sap_vm_provision](https://github.com/sap-linuxlab/community.sap_infrastructure/tree/main/roles/sap_vm_provision), for creating the following required resources: DNS, Load Balancers and Health Checks. +- [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 diff --git a/roles/sap_vm_temp_vip/tasks/main.yml b/roles/sap_vm_temp_vip/tasks/main.yml index ef5c775..f88d3a7 100644 --- a/roles/sap_vm_temp_vip/tasks/main.yml +++ b/roles/sap_vm_temp_vip/tasks/main.yml @@ -1,6 +1,12 @@ --- # Ansible role to 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 From 362901da5b76d41fd6ba3db6f3f790c7cc459ba1 Mon Sep 17 00:00:00 2001 From: Marcel Mamula Date: Thu, 26 Sep 2024 10:15:09 +0200 Subject: [PATCH 8/8] fix: remove gather_facts assert memo --- roles/sap_vm_temp_vip/tasks/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/sap_vm_temp_vip/tasks/main.yml b/roles/sap_vm_temp_vip/tasks/main.yml index f88d3a7..e1d4565 100644 --- a/roles/sap_vm_temp_vip/tasks/main.yml +++ b/roles/sap_vm_temp_vip/tasks/main.yml @@ -12,7 +12,7 @@ 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 gather_facts:true is set and sap_vm_temp_vip_default_ip is not empty." + - "Ensure that sap_vm_temp_vip_default_ip is not empty." - name: Block to ensure that only supported groups are allowed