diff --git a/roles/sap_swpm/README.md b/roles/sap_swpm/README.md
index aafa3f751..f5148f53c 100644
--- a/roles/sap_swpm/README.md
+++ b/roles/sap_swpm/README.md
@@ -110,6 +110,8 @@ It is also possible to use method 1 for creating the inifile and then replace or
- Set expiry of Linux created users to 'never'
- (Optional) Apply firewall rules for SAP Netweaver if `sap_swpm_setup_firewall` is set to `true` (Default: `false`).
+
+- (Optional) Handle the execution of SUM if SWPM started it (See Up-To-Date Installation below).
### Example
@@ -181,6 +183,16 @@ With the following tags, the role can be called to perform certain activities on
- tag `sap_swpm_update_etchosts`: Only update file `/etc/hosts` (but only if variable `sap_swpm_update_etchosts` is set to `true`).
+## Additional information
+
+### Up-To-Date Installation (UDI)
+The Software Update Manager can run on any host with an Application Server instance (e.g. NWAS ABAP PAS/AAS, NWAS JAVA CI/AAS) with correct permissions to access /usr/sap/ and /sapmnt/ directories.
+
+When using the Software Provisioning Manager (SWPM) with a Maintenance Planner Stack XML file to perform an "up-to-date installation" (UDI) - it will start the Software Update Manager (SUM) automatically at the end of the installation process. This UDI feature applies only to SAP ABAP Platform / SAP NetWeaver, and must be performed from the Primary Application Server instance (i.e. NWAS ABAP PAS, or a OneHost installation).
+
+Furthermore, during SWPM variable selection the enabling of Transport Management System (TMS) is required, see SAP Note 2522253 - SWPM can not call SUM automatically when doing the up-to-date installation.
+
+
## License
Apache 2.0
diff --git a/roles/sap_swpm/tasks/post_install.yml b/roles/sap_swpm/tasks/post_install.yml
index 65fba3cf0..33fdc96f9 100644
--- a/roles/sap_swpm/tasks/post_install.yml
+++ b/roles/sap_swpm/tasks/post_install.yml
@@ -57,3 +57,13 @@
- __sap_swpm_post_install_register_hdbuserstore_exists.stat.exists
register: __sap_swpm_post_install_register_hdbuserstore_connection
changed_when: __sap_swpm_post_install_register_hdbuserstore_connection is succeeded
+
+# Now that SWPM finished we may need to deal with SUM before continuing when sap_swpm_sum_start: 'true'
+# and if we are doing OneHost or CI/PAS installation
+# If observer mode is enabled, SWPM will wait for SUM to finish before continuing so we can't do anything here
+- name: SAP SWPM Post Install - Control SUM if required
+ ansible.builtin.include_tasks: post_install/sum_push_to_finish.yml
+ when:
+ - sap_swpm_sum_start
+ - not sap_swpm_swpm_observer_mode
+ - "'NW_ABAP_CI:' in sap_swpm_product_catalog_id or 'NW_ABAP_OneHost:' in sap_swpm_product_catalog_id"
diff --git a/roles/sap_swpm/tasks/post_install/sum_push_to_finish.yml b/roles/sap_swpm/tasks/post_install/sum_push_to_finish.yml
new file mode 100644
index 000000000..24e416a2c
--- /dev/null
+++ b/roles/sap_swpm/tasks/post_install/sum_push_to_finish.yml
@@ -0,0 +1,188 @@
+# SPDX-License-Identifier: Apache-2.0
+---
+# Will keep pushing SUM through the two key manual steps until it finishes
+# This allows to fully automate the SWPM+SUM installation process
+# Based on research by @sean-freeman
+
+# Check if the SUMup process is running, give it 5 minutes to start
+- name: Check if SAPup_real process is running (wait for 5 minutes until it starts)
+ ansible.builtin.command: pgrep -c -u "{{ sap_swpm_sid | lower }}adm" -f SAPup_real
+ register: _sapup_process
+ retries: 5
+ delay: 60
+ until: _sapup_process.rc == 0 and _sapup_process.stdout|int > 0
+ failed_when: _sapup_process.rc != 0
+ changed_when: false
+
+- name: Print SUM monitoring and action URLs
+ ansible.builtin.debug:
+ msg:
+ - "Check the following URLs for SAP Software Update Manager (SUM) monitoring or actions:"
+ - "Note: If these URLs don't work, check the sapinst.log file for the correct URLs."
+ - "SUM Monitor - https://{{ ansible_fqdn }}:1129/lmsl/sumobserver/{{ sap_swpm_sid | upper }}/monitor/index.html"
+ - "SUM Admin - https://{{ ansible_fqdn }}:1129/lmsl/sumabap/{{ sap_swpm_sid | upper }}/slui/"
+ - "SUM Admin Utilities - https://{{ ansible_fqdn }}:1129/lmsl/sumabap/{{ sap_swpm_sid | upper }}/slui_ext/"
+
+# Check the SUM status via SUMOBSEVER.XML, wait for 60 minutes until we are in BIND_PATCH phase
+- name: Checking the status of SUM (BIND_PATCH)
+ ansible.builtin.uri:
+ url: "https://localhost:1129/lmsl/sumobserver/{{ sap_swpm_sid | upper }}/analysis/SUMOBSERVER.XML"
+ method: GET
+ validate_certs: false
+ return_content: true
+ status_code: 200
+ user: "{{ sap_swpm_sid | lower }}adm"
+ password: "{{ sap_swpm_sap_sidadm_password }}"
+ register: _sap_swpm_sum_push
+ until: _sap_swpm_sum_push.status == 200 and 'SUM4ABAP|CONFIGURE|PREP_EXTENSION|BIND_PATCH|PatchSelection' in _sap_swpm_sum_push.content
+ retries: 60
+ delay: 60
+ failed_when: _sap_swpm_sum_push.status != 200 or 'SUM4ABAP|CONFIGURE|PREP_EXTENSION|BIND_PATCH|PatchSelection' not in _sap_swpm_sum_push.content
+
+# Get the config XML from SUM, repeat 3 times in case SUM is still busy and doesn't respond promptly
+- name: Get the config XML from SUM (BIND_PATCH)
+ ansible.builtin.uri:
+ url: "https://localhost:1129/slp/sumabap/{{ sap_swpm_sid | upper }}/config"
+ method: GET
+ validate_certs: false
+ return_content: true
+ status_code: 200
+ user: "{{ sap_swpm_sid | lower }}adm"
+ password: "{{ sap_swpm_sap_sidadm_password }}"
+ # headers:
+ # Cookie: "{{ _sap_swpm_sum_push.cookies_string }}"
+ register: _sap_swpm_sum_push_config
+ until: _sap_swpm_sum_push_config.status == 200
+ retries: 3
+ delay: 60
+ failed_when: _sap_swpm_sum_push_config.status != 200
+
+# Get the CSRF token from SUM by calling config
+- name: Get the CSRF token from SUM (BIND_PATCH)
+ ansible.builtin.uri:
+ url: "https://localhost:1129/slp/sumabap/{{ sap_swpm_sid | upper }}/config"
+ method: GET
+ validate_certs: false
+ return_content: false
+ status_code: 200
+ user: "{{ sap_swpm_sid | lower }}adm"
+ password: "{{ sap_swpm_sap_sidadm_password }}"
+ headers:
+ Cookie: "{{ _sap_swpm_sum_push_config.cookies_string }}"
+ X-CSRF-Token: Fetch
+ # X-Requested-With: XMLHttpRequest
+ register: _sap_swpm_sum_push
+ failed_when: _sap_swpm_sum_push.status != 200
+
+# Post the config XML back to SUM unchanged as we want to keep the default patch levels as per the XML file. This will move SUM to the next step.
+- name: Move SUM to the next step (SPAUINFO)
+ ansible.builtin.uri:
+ url: "https://localhost:1129/slp/sumabap/{{ sap_swpm_sid | upper }}/config"
+ method: POST
+ validate_certs: false
+ return_content: true
+ status_code: 200
+ user: "{{ sap_swpm_sid | lower }}adm"
+ password: "{{ sap_swpm_sap_sidadm_password }}"
+ headers:
+ Cookie: "{{ _sap_swpm_sum_push_config.cookies_string }}"
+ X-CSRF-Token: "{{ _sap_swpm_sum_push.x_csrf_token }}"
+ body_format: raw
+ body: "{{ _sap_swpm_sum_push_config.content }}"
+ register: _sap_swpm_sum_push
+ until: _sap_swpm_sum_push.status == 200
+ failed_when: _sap_swpm_sum_push.status != 200
+
+# At this point SUM should be running. This will take anything between 6-12 hours to complete.
+# Patiently check every 10 minutes to see if SUM has completed all the steps and is now in the SPAUINFO step.
+# Check the SUM status via SUMOBSEVER.XML
+- name: Checking the status of SUM (SPAUINFO) every 5 minutes
+ ansible.builtin.uri:
+ url: "https://localhost:1129/lmsl/sumobserver/{{ sap_swpm_sid | upper }}/analysis/SUMOBSERVER.XML"
+ method: GET
+ validate_certs: false
+ return_content: true
+ status_code: 200
+ user: "{{ sap_swpm_sid | lower }}adm"
+ password: "{{ sap_swpm_sap_sidadm_password }}"
+ register: _sap_swpm_sum_push
+ until: _sap_swpm_sum_push.status == 200 and ('SUM4ABAP|POST-EXECUTE|MAIN_POSTPROC|SPAUINFO|FinishSPAU' in _sap_swpm_sum_push.content or 'SUM4ABAP|MAIN_POSTCLEAN|EXIT|UnitWizard' in _sap_swpm_sum_push.content)
+ retries: 144 # 12 hours
+ delay: 300 # 5 minutes
+ failed_when: _sap_swpm_sum_push.status != 200 or ('SUM4ABAP|POST-EXECUTE|MAIN_POSTPROC|SPAUINFO|FinishSPAU' not in _sap_swpm_sum_push.content and 'SUM4ABAP|MAIN_POSTCLEAN|EXIT|UnitWizard' not in _sap_swpm_sum_push.content)
+
+# If SUM is already in MAIN_POSTCLEAN|EXIT phase it has skipped SPAUINFO and has finished
+# Process SPAUINFO if required
+- name: Check if SUM is still in SPAUINFO or has finished
+ when: "'SUM4ABAP|POST-EXECUTE|MAIN_POSTPROC|SPAUINFO|FinishSPAU' in _sap_swpm_sum_push.content"
+ block:
+ # Get the config XML from SUM, repeat 3 times in case SUM is still busy and doesn't respond promptly
+ # Need to get config XML even through we are not going to use it. This is to get the diagtime cookie.
+ - name: Get the config XML from SUM (SPAUINFO)
+ ansible.builtin.uri:
+ url: "https://localhost:1129/slp/sumabap/{{ sap_swpm_sid | upper }}/config"
+ method: GET
+ validate_certs: false
+ return_content: true
+ status_code: 200
+ user: "{{ sap_swpm_sid | lower }}adm"
+ password: "{{ sap_swpm_sap_sidadm_password }}"
+ # headers:
+ # Cookie: "{{ _sap_swpm_sum_push.cookies_string }}"
+ register: _sap_swpm_sum_push_config2
+ until: _sap_swpm_sum_push_config2.status == 200
+ retries: 3
+ delay: 60
+ failed_when: _sap_swpm_sum_push_config2.status != 200
+
+ # Get the CSRF token from SUM by calling config
+ - name: Get the CSRF token from SUM (SPAUINFO)
+ ansible.builtin.uri:
+ url: "https://localhost:1129/slp/sumabap/{{ sap_swpm_sid | upper }}/config"
+ method: GET
+ validate_certs: false
+ return_content: false
+ status_code: 200
+ user: "{{ sap_swpm_sid | lower }}adm"
+ password: "{{ sap_swpm_sap_sidadm_password }}"
+ headers:
+ Cookie: "{{ _sap_swpm_sum_push_config2.cookies_string }}"
+ X-CSRF-Token: Fetch
+ # X-Requested-With: XMLHttpRequest
+ register: _sap_swpm_sum_push2
+ failed_when: _sap_swpm_sum_push2.status != 200
+
+ # Post confirmation to SUM that no SPAU is required. This will move SUM to the next step.
+ - name: Move SUM to the next step (Past SPAUINFO)
+ ansible.builtin.uri:
+ url: "https://localhost:1129/slp/sumabap/{{ sap_swpm_sid | upper }}/config"
+ method: POST
+ validate_certs: false
+ return_content: true
+ status_code: 200
+ user: "{{ sap_swpm_sid | lower }}adm"
+ password: "{{ sap_swpm_sap_sidadm_password }}"
+ headers:
+ Cookie: "{{ _sap_swpm_sum_push_config2.cookies_string }}"
+ X-CSRF-Token: "{{ _sap_swpm_sum_push2.x_csrf_token }}"
+ body_format: raw
+ body: 'DialogueValueslp.parameter.type.SCALAR10yes'
+ register: _sap_swpm_sum_push2
+ failed_when: _sap_swpm_sum_push2.status != 200
+
+# Finally wait for SUM to finish the final steps. This shouldn't take more than 1 hour.
+# Check the SUM status via SUMOBSEVER.XML, wait for 60 minutes until the status is 'MAIN_POSTCLEAN|EXIT'
+- name: Checking the status of SUM and making sure it has finished (MAIN_POSTCLEAN|EXIT)
+ ansible.builtin.uri:
+ url: "https://localhost:1129/lmsl/sumobserver/{{ sap_swpm_sid | upper }}/analysis/SUMOBSERVER.XML"
+ method: GET
+ validate_certs: false
+ return_content: true
+ status_code: 200
+ user: "{{ sap_swpm_sid | lower }}adm"
+ password: "{{ sap_swpm_sap_sidadm_password }}"
+ register: _sap_swpm_sum_push
+ until: _sap_swpm_sum_push.status == 200 and 'SUM4ABAP|MAIN_POSTCLEAN|EXIT|UnitWizard' in _sap_swpm_sum_push.content
+ retries: 60
+ delay: 60
+ failed_when: _sap_swpm_sum_push.status != 200 or 'SUM4ABAP|MAIN_POSTCLEAN|EXIT|UnitWizard' not in _sap_swpm_sum_push.content