diff --git a/changelogs/fragments/5124-compatibility-virtualmedia-resource-location.yaml b/changelogs/fragments/5124-compatibility-virtualmedia-resource-location.yaml new file mode 100644 index 0000000000..8aacacaed4 --- /dev/null +++ b/changelogs/fragments/5124-compatibility-virtualmedia-resource-location.yaml @@ -0,0 +1,2 @@ +minor_changes: + - redfish - added new command GetVirtualMedia, VirtualMediaInsert and VirtualMediaEject to Systems category due to Redfish spec changes the virtualMedia resource location from Manager to System (https://github.com/ansible-collections/community.general/pull/5124). \ No newline at end of file diff --git a/plugins/module_utils/redfish_utils.py b/plugins/module_utils/redfish_utils.py index 9068f9b92a..464d1110d7 100644 --- a/plugins/module_utils/redfish_utils.py +++ b/plugins/module_utils/redfish_utils.py @@ -202,6 +202,16 @@ class RedfishUtils(object): def _init_session(self): pass + def _get_vendor(self): + response = self.get_request(self.root_uri + self.service_root) + if response['ret'] is False: + return {'ret': False, 'Vendor': ''} + data = response['data'] + if 'Vendor' in data: + return {'ret': True, 'Vendor': data["Vendor"]} + else: + return {'ret': True, 'Vendor': ''} + def _find_accountservice_resource(self): response = self.get_request(self.root_uri + self.service_root) if response['ret'] is False: @@ -2162,11 +2172,15 @@ class RedfishUtils(object): result["entries"] = virtualmedia_results return result - def get_multi_virtualmedia(self): + def get_multi_virtualmedia(self, resource_type='Manager'): ret = True entries = [] - resource_uris = self.manager_uris + # Given resource_type, use the proper URI + if resource_type == 'Systems': + resource_uris = self.systems_uris + elif resource_type == 'Manager': + resource_uris = self.manager_uris for resource_uri in resource_uris: virtualmedia = self.get_virtualmedia(resource_uri) @@ -2178,7 +2192,7 @@ class RedfishUtils(object): @staticmethod def _find_empty_virt_media_slot(resources, media_types, - media_match_strict=True): + media_match_strict=True, vendor=''): for uri, data in resources.items(): # check MediaTypes if 'MediaTypes' in data and media_types: @@ -2187,8 +2201,12 @@ class RedfishUtils(object): else: if media_match_strict: continue - # if ejected, 'Inserted' should be False - if (not data.get('Inserted', False)): + # Base on current Lenovo server capability, filter out slot RDOC1/2 and Remote1/2/3/4 which are not supported to Insert/Eject. + if vendor == 'Lenovo' and ('RDOC' in uri or 'Remote' in uri): + continue + # if ejected, 'Inserted' should be False and 'ImageName' cleared + if (not data.get('Inserted', False) and + not data.get('ImageName')): return uri, data return None, None @@ -2262,7 +2280,7 @@ class RedfishUtils(object): return response return {'ret': True, 'changed': True, 'msg': "VirtualMedia inserted"} - def virtual_media_insert(self, options): + def virtual_media_insert(self, options, resource_type='Manager'): param_map = { 'Inserted': 'inserted', 'WriteProtected': 'write_protected', @@ -2279,7 +2297,12 @@ class RedfishUtils(object): media_types = options.get('media_types') # locate and read the VirtualMedia resources - response = self.get_request(self.root_uri + self.manager_uri) + # Given resource_type, use the proper URI + if resource_type == 'Systems': + resource_uri = self.systems_uri + elif resource_type == 'Manager': + resource_uri = self.manager_uri + response = self.get_request(self.root_uri + resource_uri) if response['ret'] is False: return response data = response['data'] @@ -2288,7 +2311,7 @@ class RedfishUtils(object): # Some hardware (such as iLO 4) only supports the Image property on the PATCH operation # Inserted and WriteProtected are not writable - if data["FirmwareVersion"].startswith("iLO 4"): + if "FirmwareVersion" in data and data["FirmwareVersion"].startswith("iLO 4"): image_only = True # Supermicro does also not support Inserted and WriteProtected @@ -2315,12 +2338,13 @@ class RedfishUtils(object): # find an empty slot to insert the media # try first with strict media_type matching + vendor = self._get_vendor()['Vendor'] uri, data = self._find_empty_virt_media_slot( - resources, media_types, media_match_strict=True) + resources, media_types, media_match_strict=True, vendor=vendor) if not uri: # if not found, try without strict media_type matching uri, data = self._find_empty_virt_media_slot( - resources, media_types, media_match_strict=False) + resources, media_types, media_match_strict=False, vendor=vendor) if not uri: return {'ret': False, 'msg': "Unable to find an available VirtualMedia resource " @@ -2378,14 +2402,19 @@ class RedfishUtils(object): return {'ret': True, 'changed': True, 'msg': "VirtualMedia ejected"} - def virtual_media_eject(self, options): + def virtual_media_eject(self, options, resource_type='Manager'): image_url = options.get('image_url') if not image_url: return {'ret': False, 'msg': "image_url option required for VirtualMediaEject"} # locate and read the VirtualMedia resources - response = self.get_request(self.root_uri + self.manager_uri) + # Given resource_type, use the proper URI + if resource_type == 'Systems': + resource_uri = self.systems_uri + elif resource_type == 'Manager': + resource_uri = self.manager_uri + response = self.get_request(self.root_uri + resource_uri) if response['ret'] is False: return response data = response['data'] @@ -2395,7 +2424,7 @@ class RedfishUtils(object): # Some hardware (such as iLO 4) only supports the Image property on the PATCH operation # Inserted is not writable image_only = False - if data["FirmwareVersion"].startswith("iLO 4"): + if "FirmwareVersion" in data and data["FirmwareVersion"].startswith("iLO 4"): image_only = True if 'Supermicro' in data['Oem']: diff --git a/plugins/modules/remote_management/redfish/redfish_command.py b/plugins/modules/remote_management/redfish/redfish_command.py index 448f763ab1..35a12eb8c1 100644 --- a/plugins/modules/remote_management/redfish/redfish_command.py +++ b/plugins/modules/remote_management/redfish/redfish_command.py @@ -505,6 +505,20 @@ EXAMPLES = ''' username: operator password: supersecretpwd + - name: Insert Virtual Media + community.general.redfish_command: + category: Systems + command: VirtualMediaInsert + baseuri: "{{ baseuri }}" + username: "{{ username }}" + password: "{{ password }}" + virtual_media: + image_url: 'http://example.com/images/SomeLinux-current.iso' + media_types: + - CD + - DVD + resource_id: 1 + - name: Insert Virtual Media community.general.redfish_command: category: Manager @@ -519,6 +533,17 @@ EXAMPLES = ''' - DVD resource_id: BMC + - name: Eject Virtual Media + community.general.redfish_command: + category: Systems + command: VirtualMediaEject + baseuri: "{{ baseuri }}" + username: "{{ username }}" + password: "{{ password }}" + virtual_media: + image_url: 'http://example.com/images/SomeLinux-current.iso' + resource_id: 1 + - name: Eject Virtual Media community.general.redfish_command: category: Manager @@ -593,7 +618,7 @@ from ansible.module_utils.common.text.converters import to_native CATEGORY_COMMANDS_ALL = { "Systems": ["PowerOn", "PowerForceOff", "PowerForceRestart", "PowerGracefulRestart", "PowerGracefulShutdown", "PowerReboot", "SetOneTimeBoot", "EnableContinuousBootOverride", "DisableBootOverride", - "IndicatorLedOn", "IndicatorLedOff", "IndicatorLedBlink"], + "IndicatorLedOn", "IndicatorLedOff", "IndicatorLedBlink", "VirtualMediaInsert", "VirtualMediaEject"], "Chassis": ["IndicatorLedOn", "IndicatorLedOff", "IndicatorLedBlink"], "Accounts": ["AddUser", "EnableUser", "DeleteUser", "DisableUser", "UpdateUserRole", "UpdateUserPassword", "UpdateUserName", @@ -766,6 +791,10 @@ def main(): result = rf_utils.set_boot_override(boot_opts) elif command.startswith('IndicatorLed'): result = rf_utils.manage_system_indicator_led(command) + elif command == 'VirtualMediaInsert': + result = rf_utils.virtual_media_insert(virtual_media, category) + elif command == 'VirtualMediaEject': + result = rf_utils.virtual_media_eject(virtual_media, category) elif category == "Chassis": result = rf_utils._find_chassis_resource() @@ -814,9 +843,9 @@ def main(): elif command == 'ClearLogs': result = rf_utils.clear_logs() elif command == 'VirtualMediaInsert': - result = rf_utils.virtual_media_insert(virtual_media) + result = rf_utils.virtual_media_insert(virtual_media, category) elif command == 'VirtualMediaEject': - result = rf_utils.virtual_media_eject(virtual_media) + result = rf_utils.virtual_media_eject(virtual_media, category) elif category == "Update": # execute only if we find UpdateService resources diff --git a/plugins/modules/remote_management/redfish/redfish_info.py b/plugins/modules/remote_management/redfish/redfish_info.py index 76fbb3fe35..4cb42711ad 100644 --- a/plugins/modules/remote_management/redfish/redfish_info.py +++ b/plugins/modules/remote_management/redfish/redfish_info.py @@ -118,6 +118,19 @@ EXAMPLES = ''' ansible.builtin.debug: msg: "{{ result.redfish_facts.virtual_media.entries | to_nice_json }}" + - name: Get Virtual Media information from Systems + community.general.redfish_info: + category: Systems + command: GetVirtualMedia + baseuri: "{{ baseuri }}" + username: "{{ username }}" + password: "{{ password }}" + register: result + + - name: Print fetched information + ansible.builtin.debug: + msg: "{{ result.redfish_facts.virtual_media.entries | to_nice_json }}" + - name: Get Volume Inventory community.general.redfish_info: category: Systems @@ -303,7 +316,7 @@ CATEGORY_COMMANDS_ALL = { "Systems": ["GetSystemInventory", "GetPsuInventory", "GetCpuInventory", "GetMemoryInventory", "GetNicInventory", "GetHealthReport", "GetStorageControllerInventory", "GetDiskInventory", "GetVolumeInventory", - "GetBiosAttributes", "GetBootOrder", "GetBootOverride"], + "GetBiosAttributes", "GetBootOrder", "GetBootOverride", "GetVirtualMedia"], "Chassis": ["GetFanInventory", "GetPsuInventory", "GetChassisPower", "GetChassisThermals", "GetChassisInventory", "GetHealthReport"], "Accounts": ["ListUsers"], @@ -420,6 +433,8 @@ def main(): result["boot_override"] = rf_utils.get_multi_boot_override() elif command == "GetHealthReport": result["health_report"] = rf_utils.get_multi_system_health_report() + elif command == "GetVirtualMedia": + result["virtual_media"] = rf_utils.get_multi_virtualmedia(category) elif category == "Chassis": # execute only if we find Chassis resource @@ -485,7 +500,7 @@ def main(): if command == "GetManagerNicInventory": result["manager_nics"] = rf_utils.get_multi_nic_inventory(category) elif command == "GetVirtualMedia": - result["virtual_media"] = rf_utils.get_multi_virtualmedia() + result["virtual_media"] = rf_utils.get_multi_virtualmedia(category) elif command == "GetLogs": result["log"] = rf_utils.get_logs() elif command == "GetNetworkProtocols":