Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelogs/fragments/37_sap_control_add_system_functions
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
minor_changes:
- sap_control - add the mechanisms to handle asynchronous sapcontrol functions
- sap_control - add UpdateSystem, RestartSystem, StartSystem, StopSystem functions
3 changes: 2 additions & 1 deletion roles/sap_control/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ This Ansible Role executes basic SAP administration tasks on Linux operating sys
This Ansible Role executes basic SAP administration tasks on Linux operating systems, including:
- Start/Stop/Restart of SAP HANA Database Server
- Start/Stop/Restart of SAP NetWeaver Application Server
- Start/Stop/Restart/Update of SAP Netweaver System
- Multiple Automatic discovery and Start/Stop/Restart of SAP HANA Database Server or SAP NetWeaver Application Server

## Example execution
Expand Down Expand Up @@ -62,7 +63,7 @@ Assumptions for executing this role include:
| :--- |:--- | :--- |
| `SID` | SAP system SID | no, only if you are targetting a single SAP system|
| `nowait` | Default: `false` | no, use only when absolutely sure! This will bypass all waiting and ignore all necessary steps for a graceful stop / start|
| `sap_control_function` | Function to execute:<br/><ul><li>`restart_all_sap`</li><li>`restart_all_nw`</li><li>`restart_all_hana`</li><li>`restart_sap_nw`</li><li>`restart_sap_hana`</li><li>`stop_all_sap`</li><li>`start_all_sap`</li><li>`stop_all_nw`</li><li>`start_all_nw`</li><li>`stop_all_hana`</li><li>`start_all_hana`</li><li>`stop_sap_nw`</li><li>`start_sap_nw`</li><li>`stop_sap_hana`</li><li>`start_sap_hana`</li></ul> | yes, only this is required to detect the Instance Number which is used with SAP Host Agent `sapcontrol` CLI<br/><br/><br/>_Note: Executions using `all` will automatically detect any System IDs and corresponding Instance Numbers_ |
| `sap_control_function` | Function to execute:<br/><ul><li>`restart_all_sap`</li><li>`restart_all_nw`</li><li>`restart_all_hana`</li><li>`restart_sap_nw`</li><li>`restart_sap_hana`</li><li>`stop_all_sap`</li><li>`start_all_sap`</li><li>`stop_all_nw`</li><li>`start_all_nw`</li><li>`stop_all_hana`</li><li>`start_all_hana`</li><li>`stop_sap_nw`</li><li>`start_sap_nw`</li><li>`stop_sap_hana`</li><li>`start_sap_hana`</li><li>`restartsystem_all_nw`</li><li>`updatesystem_all_nw`</li><li>`startsystem_all_nw`</li><li>`stopsystem_all_nw`</li></ul> | yes, only this is required to detect the Instance Number which is used with SAP Host Agent `sapcontrol` CLI<br/><br/><br/>_Note: Executions using `all` will automatically detect any System IDs and corresponding Instance Numbers_ |

## Ansible Role workflow and structure

Expand Down
69 changes: 69 additions & 0 deletions roles/sap_control/defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,41 @@ sap_control_name_header: "initial"
nowait: false
sap_control_start: "StartWait 180 2"
sap_control_stop: "StopWait 180 2"
sap_control_startsystem: "StartSystem ALL 180" # function StartSystem waittimeout
sap_control_stopsystem: "StopSystem ALL 180 480" # function StopSystem waittimeout softtimeout
sap_control_restartsystem: "RestartSystem ALL 180 480" # function RestartSystem waittimeout softtimeout
sap_control_updatesystem: "UpdateSystem 180 480 0" # function UpdateSystem waittimeout softtimeout force
sap_control_waitforstopped: "WaitforStopped 180 2" # function WaitforStopped waittimeout delay
sap_control_waitforstarted: "WaitforStarted 180 2" # function WaitforStarted waittimeout delay
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These waitforstarted/waitforstopped aren't used anymore, I was using these instead of the current "waitforasync".
They technically work because they loop through every instance and wait for each of them to be started or stopped, but it's not very clean.


# Parameters to handle async functions in sapcontrol_async.yml

sap_control_startsystem_waitforasync:
test_function: "GetSystemInstanceList"
retries: 60
delay: 10
until_false: 'GRAY\s*$|RED\s*$|YELLOW\s*$'
until_true: 'GREEN\s*$'

sap_control_restartsystem_waitforasync:
test_function: "GetSystemInstanceList"
retries: 60
delay: 10
until_false: 'GRAY\s*$|RED\s*$|YELLOW\s*$'
until_true: 'GREEN\s*$'

sap_control_stopsystem_waitforasync:
test_function: "GetSystemInstanceList"
retries: 60
delay: 10
until_false: 'GREEN\s*$|RED\s*$|YELLOW\s*$'
until_true: 'GRAY\s*$'

sap_control_updatesystem_waitforasync:
test_function: "GetSystemUpdateList"
retries: 60
delay: 10
until_false: 'GRAY\s*$|RED\s*$|YELLOW\s*$|GREEN\s*$'

# get_all_sap_sid_dir_nw: "/sapmnt"
# get_all_sap_sid_dir_hana: "/hana/shared"
Expand All @@ -22,6 +57,14 @@ sap_control_instance_type_sortorder:

# Functions
sap_control_functions_list:
- restartsystem_all_nw
- updatesystem_all_nw
- startsystem_all_nw
- stopsystem_all_nw
- restartsystem_sap_nw

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nbttmbrg Did you test these changes?
None of these functions have corresponding var_list so they will fail.

  - restartsystem_sap_nw
  - updatesystem_sap_nw
  - startsystem_sap_nw
  - stopsystem_sap_nw
fatal: [b01hana]: FAILED! =>
    changed: false
    msg: 'Task failed: object of type ''dict'' has no attribute ''startsystem_sap_nw_list'''

- updatesystem_sap_nw
- startsystem_sap_nw
- stopsystem_sap_nw
- restart_all_sap
- stop_all_sap
- start_all_sap
Expand All @@ -38,7 +81,33 @@ sap_control_functions_list:
- stop_sap_hana
- start_sap_hana


# Functions flow
restartsystem_all_nw_list:
- sap_control_function_current: "nw_restartsystem"

startsystem_all_nw_list:
- sap_control_function_current: "nw_startsystem"

stopsystem_all_nw_list:
- sap_control_function_current: "nw_stopsystem"

updatesystem_all_nw_list:
- sap_control_function_current: "nw_startsystem"
- sap_control_function_current: "nw_updatesystem"

restartsystem_sap_nw_list:
- sap_control_function_current: "nw_restartsystem"

updatesystem_sap_nw_list:
- sap_control_function_current: "nw_updatesystem"

startsystem_sap_nw_list:
- sap_control_function_current: "nw_startsystem"

stopsystem_sap_nw_list:
- sap_control_function_current: "nw_stopsystem"

restart_all_sap_list:
- sap_control_function_current: "nw_stop"
- sap_control_function_current: "hana_stop"
Expand Down
16 changes: 12 additions & 4 deletions roles/sap_control/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,18 @@
ansible.builtin.debug:
msg:
- "Starting sap_control with the following parameters: "
- "{{ sap_control_function }}"
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed this because it made little sense in my situation and couldn't figure out how to efficiently display only what was used. To be honnest I don't really get when this is useful at all

- "{{ sap_control_start }}"
- "{{ sap_control_stop }}"
- "{{ nowait }}"
- "Function: {{ sap_control_function }}"
- "Standard commands:"
- " Start: {{ sap_control_start }}"
- " Stop: {{ sap_control_stop }}"
- "System commands (if applicable):"
- " StartSystem: {{ sap_control_startsystem }}"
- " StopSystem: {{ sap_control_stopsystem }}"
- " RestartSystem: {{ sap_control_restartsystem }}"
- " UpdateSystem: {{ sap_control_updatesystem }}"
- " WaitforStopped: {{ sap_control_waitforstopped }}"
- " WaitforStarted: {{ sap_control_waitforstarted }}"
- "NoWait: {{ nowait }}"

# Start SAP Control
- name: SAP Control
Expand Down
15 changes: 15 additions & 0 deletions roles/sap_control/tasks/prepare.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,18 @@
loop: "{{ sorted_sap_facts }}"
when:
- item.InstanceType | lower == sap_type | lower
- not funct_type is match('.*system')

- name: Prepare - SAP Control for system functions
vars:
sap_control_execute_sid: "{{ item.SID }}"
sap_control_execute_type: "{{ item.Type }}"
sap_control_execute_instance_nr: "{{ item.NR }}"
sap_control_execute_instance_type: "{{ item.InstanceType }}"
ansible.builtin.include_tasks: "sapcontrol.yml"
loop: "{{ sorted_sap_facts }}"
when:
- item.InstanceType | lower == sap_type | lower
- funct_type is match('.*system')
- item.TYPE | lower == 'ascs'
or item.TYPE | lower == 'scs'
8 changes: 8 additions & 0 deletions roles/sap_control/tasks/sapcontrol.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@
register: sapcontrol_status
failed_when: "'FAIL' in sapcontrol_status.stdout"

# Include sapcontrol async tasks
- name: SAP {{ sap_control_name_header }} - Include async tasks
vars:
async_function_dict: "{{ vars['sap_control_' + funct_type + '_waitforasync'] }}"
ansible.builtin.include_tasks: sapcontrol_async.yml
when:
- funct_type is match('.*system')

# Cleanipc
- name: SAP {{ sap_control_name_header }} - Cleanipc
ansible.builtin.include_tasks: functions/cleanipc.yml
Expand Down
55 changes: 55 additions & 0 deletions roles/sap_control/tasks/sapcontrol_async.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
---
- name: Wait for status to change if using restart or update
when: funct_type is match('restart|update')
block:
- name: SAP {{ sap_control_name_header }} - Getting current system state
ansible.builtin.shell: |
source ~/.profile && sapcontrol -nr {{ passed_sap_nr }} -function {{ async_function_dict.test_function }}
args:
executable: /bin/bash
become: true
become_user: "{{ passed_sap_sid | lower }}adm"
register: initial_test_function_result

- name: SAP {{ sap_control_name_header }} - Waiting for state to change before polling
ansible.builtin.shell: |
source ~/.profile && sapcontrol -nr {{ passed_sap_nr }} -function {{ async_function_dict.test_function }}
args:
executable: /bin/bash
become: true
become_user: "{{ passed_sap_sid | lower }}adm"
register: wait_for_change_result
retries: "{{ async_function_dict.retries | default(0) | int }}"
delay: "{{ async_function_dict.delay | default(0) | int }}"
until: >
(wait_for_change_result.stdout | regex_findall('GREEN|YELLOW|GRAY|RED', multiline=True) | sort | join(','))
!= (initial_test_function_result.stdout | regex_findall('GREEN|YELLOW|GRAY|RED', multiline=True) | sort | join(','))

- name: Pause for 20 Seconds to ensure the async function is started
ansible.builtin.wait_for:
timeout: 20

- name: SAP {{ sap_control_name_header }} - Checking if Async action is over by executing sapcontrol -nr {{ passed_sap_nr }} -function {{ async_function_dict.test_function }}
ansible.builtin.shell: |
source ~/.profile && sapcontrol -nr {{ passed_sap_nr }} -function {{ async_function_dict.test_function }}
args:
executable: /bin/bash
become: true
become_user: "{{ passed_sap_sid | lower }}adm"
register: test_function_result
retries: "{{ async_function_dict.retries | default(0) | int }}"
delay: "{{ async_function_dict.delay | default(0) | int }}"
until: >

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

regex_search is no longer truthy in ansible-core 2.19 so you must include check for type or length.
When value is found = value.
When value is not found = NoneType

task path: /home/mmamula/ansible_collections/community/sap_operations/roles/sap_control/tasks/sapcontrol_async.yml:6
fatal: [b01hana]: FAILED! =>
    changed: false
    msg: 'Task failed: Conditional result was ''GREEN'' of type ''str'', which evaluates
        to True. Conditionals must have a boolean result.'

This is example from my recent PR where I had to deal with this:

              failed_when:
                - __sap_ha_pacemaker_cluster_register_ers_ha_failover_config_restart.stdout | d('') | regex_search('^HAActive:\\s+FALSE$', multiline=True) is not none

(async_function_dict.until_false is not defined
or async_function_dict.until_false is defined
and test_function_result.stdout | regex_search(async_function_dict.until_false, multiline=True) is none) and
(async_function_dict.until_true is not defined or
async_function_dict.until_true is defined
and test_function_result.stdout | regex_search(async_function_dict.until_true, multiline=True) is not none)

- name: Debug stdout
ansible.builtin.debug:
msg: |
Async function {{ async_function_dict.test_function }} for SAP SID {{ passed_sap_sid }}
is done with result:
{{ test_function_result.stdout }}