Repository: ansible-collections/community.general

Issue:
redfish_command: allow setting the BootSourceOverrideEnabled property
##### SUMMARY
Currently, the redfish_utils module forces the BootSourceOverrideEnabled Redfish property to "Once" in the  redfish_command module. The Redfish schema supports:

```
Once
Continuous
Disabled
```

##### ISSUE TYPE
- Feature Idea

##### COMPONENT NAME
redfish_command
redfish_utils

##### ADDITIONAL INFORMATION
Continuous is helpful as it allows setting a temporary boot override over multiple reboots until disabled, without permanently modifying the boot order.


Discussion:
Files identified in the description:
* [`plugins/module_utils/redfish_utils.py`](https://github.com/ansible-collections/community.general/blob/master/plugins/module_utils/redfish_utils.py)
* [`plugins/modules/remote_management/redfish/redfish_command.py`](https://github.com/ansible-collections/community.general/blob/master/plugins/modules/remote_management/redfish/redfish_command.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @billdodd @jose-delarosa @mraineri @tomasg2012
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->

Resolving patch:
diff --git a/plugins/module_utils/redfish_utils.py b/plugins/module_utils/redfish_utils.py
--- a/plugins/module_utils/redfish_utils.py
+++ b/plugins/module_utils/redfish_utils.py
@@ -1501,13 +1501,18 @@ def set_bios_default_settings(self):
             return response
         return {'ret': True, 'changed': True, 'msg': "Set BIOS to default settings"}
 
-    def set_one_time_boot_device(self, bootdevice, uefi_target, boot_next):
+    def set_boot_override(self, boot_opts):
         result = {}
         key = "Boot"
 
-        if not bootdevice:
+        bootdevice = boot_opts.get('bootdevice')
+        uefi_target = boot_opts.get('uefi_target')
+        boot_next = boot_opts.get('boot_next')
+        override_enabled = boot_opts.get('override_enabled')
+
+        if not bootdevice and override_enabled != 'Disabled':
             return {'ret': False,
-                    'msg': "bootdevice option required for SetOneTimeBoot"}
+                    'msg': "bootdevice option required for temporary boot override"}
 
         # Search for 'key' entry and extract URI from it
         response = self.get_request(self.root_uri + self.systems_uri)
@@ -1530,21 +1535,27 @@ def set_one_time_boot_device(self, bootdevice, uefi_target, boot_next):
                                (bootdevice, allowable_values)}
 
         # read existing values
-        enabled = boot.get('BootSourceOverrideEnabled')
+        cur_enabled = boot.get('BootSourceOverrideEnabled')
         target = boot.get('BootSourceOverrideTarget')
         cur_uefi_target = boot.get('UefiTargetBootSourceOverride')
         cur_boot_next = boot.get('BootNext')
 
-        if bootdevice == 'UefiTarget':
+        if override_enabled == 'Disabled':
+            payload = {
+                'Boot': {
+                    'BootSourceOverrideEnabled': override_enabled
+                }
+            }
+        elif bootdevice == 'UefiTarget':
             if not uefi_target:
                 return {'ret': False,
                         'msg': "uefi_target option required to SetOneTimeBoot for UefiTarget"}
-            if enabled == 'Once' and target == bootdevice and uefi_target == cur_uefi_target:
+            if override_enabled == cur_enabled and target == bootdevice and uefi_target == cur_uefi_target:
                 # If properties are already set, no changes needed
                 return {'ret': True, 'changed': False}
             payload = {
                 'Boot': {
-                    'BootSourceOverrideEnabled': 'Once',
+                    'BootSourceOverrideEnabled': override_enabled,
                     'BootSourceOverrideTarget': bootdevice,
                     'UefiTargetBootSourceOverride': uefi_target
                 }
@@ -1553,23 +1564,23 @@ def set_one_time_boot_device(self, bootdevice, uefi_target, boot_next):
             if not boot_next:
                 return {'ret': False,
                         'msg': "boot_next option required to SetOneTimeBoot for UefiBootNext"}
-            if enabled == 'Once' and target == bootdevice and boot_next == cur_boot_next:
+            if cur_enabled == override_enabled and target == bootdevice and boot_next == cur_boot_next:
                 # If properties are already set, no changes needed
                 return {'ret': True, 'changed': False}
             payload = {
                 'Boot': {
-                    'BootSourceOverrideEnabled': 'Once',
+                    'BootSourceOverrideEnabled': override_enabled,
                     'BootSourceOverrideTarget': bootdevice,
                     'BootNext': boot_next
                 }
             }
         else:
-            if enabled == 'Once' and target == bootdevice:
+            if cur_enabled == override_enabled and target == bootdevice:
                 # If properties are already set, no changes needed
                 return {'ret': True, 'changed': False}
             payload = {
                 'Boot': {
-                    'BootSourceOverrideEnabled': 'Once',
+                    'BootSourceOverrideEnabled': override_enabled,
                     'BootSourceOverrideTarget': bootdevice
                 }
             }
diff --git a/plugins/modules/remote_management/redfish/redfish_command.py b/plugins/modules/remote_management/redfish/redfish_command.py
--- a/plugins/modules/remote_management/redfish/redfish_command.py
+++ b/plugins/modules/remote_management/redfish/redfish_command.py
@@ -238,6 +238,21 @@
       username: "{{ username }}"
       password: "{{ password }}"
 
+  - name: Set persistent boot device override
+    community.general.redfish_command:
+      category: Systems
+      command: EnableContinuousBootOverride
+      resource_id: 437XR1138R2
+      bootdevice: "{{ bootdevice }}"
+      baseuri: "{{ baseuri }}"
+      username: "{{ username }}"
+      password: "{{ password }}"
+
+  - name: Disable persistent boot device override
+    community.general.redfish_command:
+      category: Systems
+      command: DisableBootOverride
+
   - name: Set chassis indicator LED to blink
     community.general.redfish_command:
       category: Chassis
@@ -442,7 +457,7 @@
 # More will be added as module features are expanded
 CATEGORY_COMMANDS_ALL = {
     "Systems": ["PowerOn", "PowerForceOff", "PowerForceRestart", "PowerGracefulRestart",
-                "PowerGracefulShutdown", "PowerReboot", "SetOneTimeBoot"],
+                "PowerGracefulShutdown", "PowerReboot", "SetOneTimeBoot", "EnableContinuousBootOverride", "DisableBootOverride"],
     "Chassis": ["IndicatorLedOn", "IndicatorLedOff", "IndicatorLedBlink"],
     "Accounts": ["AddUser", "EnableUser", "DeleteUser", "DisableUser",
                  "UpdateUserRole", "UpdateUserPassword", "UpdateUserName",
@@ -530,6 +545,13 @@ def main():
         'update_creds': module.params['update_creds']
     }
 
+    # Boot override options
+    boot_opts = {
+        'bootdevice': module.params['bootdevice'],
+        'uefi_target': module.params['uefi_target'],
+        'boot_next': module.params['boot_next']
+    }
+
     # VirtualMedia options
     virtual_media = module.params['virtual_media']
 
@@ -579,10 +601,14 @@ def main():
             if "Power" in command:
                 result = rf_utils.manage_system_power(command)
             elif command == "SetOneTimeBoot":
-                result = rf_utils.set_one_time_boot_device(
-                    module.params['bootdevice'],
-                    module.params['uefi_target'],
-                    module.params['boot_next'])
+                boot_opts['override_enabled'] = 'Once'
+                result = rf_utils.set_boot_override(boot_opts)
+            elif command == "EnableContinuousBootOverride":
+                boot_opts['override_enabled'] = 'Continuous'
+                result = rf_utils.set_boot_override(boot_opts)
+            elif command == "DisableBootOverride":
+                boot_opts['override_enabled'] = 'Disabled'
+                result = rf_utils.set_boot_override(boot_opts)
 
     elif category == "Chassis":
         result = rf_utils._find_chassis_resource()


Test patch: