Skip to content

Commit

Permalink
feat: Replaced shell with ansible tasks
Browse files Browse the repository at this point in the history
Dynamic execution based on groups
Calculation of missing inputs
Idempotency checks before execution
  • Loading branch information
marcelmamula committed Sep 23, 2024
1 parent eef145f commit 278602f
Show file tree
Hide file tree
Showing 7 changed files with 413 additions and 339 deletions.
58 changes: 58 additions & 0 deletions roles/sap_vm_temp_vip/INPUT_PARAMETERS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
## Input Parameters for sap_vm_temp_vip Ansible Role
<!-- BEGIN Role Input Parameters -->
### 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.</br>
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.</br>
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.</br>
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.</br>
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.

<!-- END Role Input Parameters -->
133 changes: 65 additions & 68 deletions roles/sap_vm_temp_vip/README.md
Original file line number Diff line number Diff line change
@@ -1,81 +1,78 @@
`Beta`

<!-- BEGIN Title -->
# sap_vm_temp_vip Ansible Role
<!-- END Title -->

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
<!-- BEGIN 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.
<!-- END Description -->

## Prerequisites
<!-- BEGIN 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:

Check failure on line 22 in roles/sap_vm_temp_vip/README.md

View workflow job for this annotation

GitHub Actions / Check for spelling errors

Dependant ==> Dependent
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.

Check failure on line 23 in roles/sap_vm_temp_vip/README.md

View workflow job for this annotation

GitHub Actions / Check for spelling errors

dependant ==> dependent
<!-- END Prerequisites -->

## 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

<!-- BEGIN Execution -->
Role can be execute separately or as part of [ansible.playbooks_for_sap](https://github.com/sap-linuxlab/ansible.playbooks_for_sap) playbooks.
<!-- END Execution -->

### Execution Flow
<!-- BEGIN 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.
<!-- END Execution Flow -->

### Example
<!-- BEGIN Execution 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
```
<!-- END Execution Example -->
<!-- BEGIN Role Tags -->
<!-- END Role Tags -->
<!-- BEGIN Further Information →
<!-- END Further Information -->
## License

<!-- BEGIN License -->
Apache 2.0
<!-- END License -->
## Maintainers
<!-- BEGIN Maintainers -->
- [Sean Freeman](https://github.com/sean-freeman)
- [Marcel Mamula](https://github.com/marcelmamula)
<!-- END Maintainers -->
## 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)
7 changes: 7 additions & 0 deletions roles/sap_vm_temp_vip/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -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('') }}"
Expand Down
100 changes: 100 additions & 0 deletions roles/sap_vm_temp_vip/tasks/get_temp_vip_details.yml
Original file line number Diff line number Diff line change
@@ -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 -%}
26 changes: 24 additions & 2 deletions roles/sap_vm_temp_vip/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -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')
Loading

0 comments on commit 278602f

Please sign in to comment.