diff --git a/lib/ansible/module_utils/redfish_utils.py b/lib/ansible/module_utils/redfish_utils.py index 1a5b99dd462..b34bfd7be05 100644 --- a/lib/ansible/module_utils/redfish_utils.py +++ b/lib/ansible/module_utils/redfish_utils.py @@ -17,15 +17,24 @@ PATCH_HEADERS = {'content-type': 'application/json', 'accept': 'application/json 'OData-Version': '4.0'} DELETE_HEADERS = {'accept': 'application/json', 'OData-Version': '4.0'} +DEPRECATE_MSG = 'Issuing a data modification command without specifying the '\ + 'ID of the target %(resource)s resource when there is more '\ + 'than one %(resource)s will use the first one in the '\ + 'collection. Use the `resource_id` option to specify the '\ + 'target %(resource)s ID' + class RedfishUtils(object): - def __init__(self, creds, root_uri, timeout, module): + def __init__(self, creds, root_uri, timeout, module, resource_id=None, + data_modification=False): self.root_uri = root_uri self.creds = creds self.timeout = timeout self.module = module self.service_root = '/redfish/v1/' + self.resource_id = resource_id + self.data_modification = data_modification self._init_session() # The following functions are to send GET/POST/PATCH/DELETE requests @@ -198,6 +207,16 @@ class RedfishUtils(object): self.sessions_uri = sessions return {'ret': True} + def _get_resource_uri_by_id(self, uris, id_prop): + for uri in uris: + response = self.get_request(self.root_uri + uri) + if response['ret'] is False: + continue + data = response['data'] + if id_prop == data.get('Id'): + return uri + return None + def _find_systems_resource(self): response = self.get_request(self.root_uri + self.service_root) if response['ret'] is False: @@ -214,6 +233,18 @@ class RedfishUtils(object): return { 'ret': False, 'msg': "ComputerSystem's Members array is either empty or missing"} + self.systems_uri = self.systems_uris[0] + if self.data_modification: + if self.resource_id: + self.systems_uri = self._get_resource_uri_by_id(self.systems_uris, + self.resource_id) + if not self.systems_uri: + return { + 'ret': False, + 'msg': "System resource %s not found" % self.resource_id} + elif len(self.systems_uris) > 1: + self.module.deprecate(DEPRECATE_MSG % {'resource': 'System'}, + version='2.13') return {'ret': True} def _find_updateservice_resource(self): @@ -245,16 +276,28 @@ class RedfishUtils(object): data = response['data'] if 'Chassis' not in data: return {'ret': False, 'msg': "Chassis resource not found"} - else: - chassis = data["Chassis"]["@odata.id"] - response = self.get_request(self.root_uri + chassis) - if response['ret'] is False: - return response - data = response['data'] - for member in data[u'Members']: - chassis_service.append(member[u'@odata.id']) - self.chassis_uri_list = chassis_service - return {'ret': True} + chassis = data["Chassis"]["@odata.id"] + response = self.get_request(self.root_uri + chassis) + if response['ret'] is False: + return response + self.chassis_uri_list = [ + i['@odata.id'] for i in response['data'].get('Members', [])] + if not self.chassis_uri_list: + return {'ret': False, + 'msg': "Chassis Members array is either empty or missing"} + self.chassis_uri = self.chassis_uri_list[0] + if self.data_modification: + if self.resource_id: + self.chassis_uri = self._get_resource_uri_by_id(self.chassis_uri_list, + self.resource_id) + if not self.chassis_uri: + return { + 'ret': False, + 'msg': "Chassis resource %s not found" % self.resource_id} + elif len(self.chassis_uri_list) > 1: + self.module.deprecate(DEPRECATE_MSG % {'resource': 'Chassis'}, + version='2.13') + return {'ret': True} def _find_managers_resource(self): response = self.get_request(self.root_uri + self.service_root) @@ -263,16 +306,28 @@ class RedfishUtils(object): data = response['data'] if 'Managers' not in data: return {'ret': False, 'msg': "Manager resource not found"} - else: - manager = data["Managers"]["@odata.id"] - response = self.get_request(self.root_uri + manager) - if response['ret'] is False: - return response - data = response['data'] - for member in data[u'Members']: - manager_service = member[u'@odata.id'] - self.manager_uri = manager_service - return {'ret': True} + manager = data["Managers"]["@odata.id"] + response = self.get_request(self.root_uri + manager) + if response['ret'] is False: + return response + self.manager_uri_list = [ + i['@odata.id'] for i in response['data'].get('Members', [])] + if not self.manager_uri_list: + return {'ret': False, + 'msg': "Managers Members array is either empty or missing"} + self.manager_uri = self.manager_uri_list[0] + if self.data_modification: + if self.resource_id: + self.manager_uri = self._get_resource_uri_by_id(self.manager_uri_list, + self.resource_id) + if not self.manager_uri: + return { + 'ret': False, + 'msg': "Manager resource %s not found" % self.resource_id} + elif len(self.manager_uri_list) > 1: + self.module.deprecate(DEPRECATE_MSG % {'resource': 'Manager'}, + version='2.13') + return {'ret': True} def get_logs(self): log_svcs_uri_list = [] @@ -696,7 +751,7 @@ class RedfishUtils(object): return {'ret': False, 'msg': 'Invalid Command (%s)' % command} # read the system resource and get the current power state - response = self.get_request(self.root_uri + self.systems_uris[0]) + response = self.get_request(self.root_uri + self.systems_uri) if response['ret'] is False: return response data = response['data'] @@ -1317,7 +1372,7 @@ class RedfishUtils(object): key = "Bios" # Search for 'key' entry and extract URI from it - response = self.get_request(self.root_uri + self.systems_uris[0]) + response = self.get_request(self.root_uri + self.systems_uri) if response['ret'] is False: return response result['ret'] = True @@ -1350,7 +1405,7 @@ class RedfishUtils(object): 'msg': "bootdevice option required for SetOneTimeBoot"} # Search for 'key' entry and extract URI from it - response = self.get_request(self.root_uri + self.systems_uris[0]) + response = self.get_request(self.root_uri + self.systems_uri) if response['ret'] is False: return response result['ret'] = True @@ -1414,7 +1469,7 @@ class RedfishUtils(object): } } - response = self.patch_request(self.root_uri + self.systems_uris[0], payload) + response = self.patch_request(self.root_uri + self.systems_uri, payload) if response['ret'] is False: return response return {'ret': True, 'changed': True} @@ -1424,7 +1479,7 @@ class RedfishUtils(object): key = "Bios" # Search for 'key' entry and extract URI from it - response = self.get_request(self.root_uri + self.systems_uris[0]) + response = self.get_request(self.root_uri + self.systems_uri) if response['ret'] is False: return response result['ret'] = True @@ -1473,8 +1528,7 @@ class RedfishUtils(object): return {'ret': False, 'msg': "boot_order list required for SetBootOrder command"} - # TODO(billdodd): change to self.systems_uri after PR 62921 merged - systems_uri = self.systems_uris[0] + systems_uri = self.systems_uri response = self.get_request(self.root_uri + systems_uri) if response['ret'] is False: return response @@ -1512,8 +1566,7 @@ class RedfishUtils(object): return {'ret': True, 'changed': True, 'msg': "BootOrder set"} def set_default_boot_order(self): - # TODO(billdodd): change to self.systems_uri after PR 62921 merged - systems_uri = self.systems_uris[0] + systems_uri = self.systems_uri response = self.get_request(self.root_uri + systems_uri) if response['ret'] is False: return response diff --git a/lib/ansible/modules/remote_management/redfish/idrac_redfish_command.py b/lib/ansible/modules/remote_management/redfish/idrac_redfish_command.py index fa02be2ee62..5399fe16c8a 100644 --- a/lib/ansible/modules/remote_management/redfish/idrac_redfish_command.py +++ b/lib/ansible/modules/remote_management/redfish/idrac_redfish_command.py @@ -52,6 +52,12 @@ options: default: 10 type: int version_added: '2.8' + resource_id: + required: false + description: + - The ID of the System, Manager or Chassis to modify + type: str + version_added: '2.10' author: "Jose Delarosa (@jose-delarosa)" ''' @@ -61,6 +67,7 @@ EXAMPLES = ''' idrac_redfish_command: category: Systems command: CreateBiosConfigJob + resource_id: System.Embedded.1 baseuri: "{{ baseuri }}" username: "{{ username }}" password: "{{ password }}" @@ -137,7 +144,8 @@ def main(): baseuri=dict(required=True), username=dict(required=True), password=dict(required=True, no_log=True), - timeout=dict(type='int', default=10) + timeout=dict(type='int', default=10), + resource_id=dict() ), supports_check_mode=False ) @@ -152,9 +160,13 @@ def main(): # timeout timeout = module.params['timeout'] + # System, Manager or Chassis ID to modify + resource_id = module.params['resource_id'] + # Build root URI root_uri = "https://" + module.params['baseuri'] - rf_utils = IdracRedfishUtils(creds, root_uri, timeout, module) + rf_utils = IdracRedfishUtils(creds, root_uri, timeout, module, + resource_id=resource_id, data_modification=True) # Check that Category is valid if category not in CATEGORY_COMMANDS_ALL: diff --git a/lib/ansible/modules/remote_management/redfish/idrac_redfish_config.py b/lib/ansible/modules/remote_management/redfish/idrac_redfish_config.py index 6c512a35126..16a50619aac 100644 --- a/lib/ansible/modules/remote_management/redfish/idrac_redfish_config.py +++ b/lib/ansible/modules/remote_management/redfish/idrac_redfish_config.py @@ -63,6 +63,12 @@ options: - Timeout in seconds for URL requests to iDRAC controller default: 10 type: int + resource_id: + required: false + description: + - The ID of the System, Manager or Chassis to modify + type: str + version_added: '2.10' author: "Jose Delarosa (@jose-delarosa)" ''' @@ -72,6 +78,7 @@ EXAMPLES = ''' idrac_redfish_config: category: Manager command: SetManagerAttributes + resource_id: iDRAC.Embedded.1 manager_attribute_name: NTPConfigGroup.1.NTPEnable manager_attribute_value: Enabled baseuri: "{{ baseuri }}" @@ -81,6 +88,7 @@ EXAMPLES = ''' idrac_redfish_config: category: Manager command: SetManagerAttributes + resource_id: iDRAC.Embedded.1 manager_attribute_name: NTPConfigGroup.1.NTP1 manager_attribute_value: "{{ ntpserver1 }}" baseuri: "{{ baseuri }}" @@ -90,6 +98,7 @@ EXAMPLES = ''' idrac_redfish_config: category: Manager command: SetManagerAttributes + resource_id: iDRAC.Embedded.1 manager_attribute_name: Time.1.Timezone manager_attribute_value: "{{ timezone }}" baseuri: "{{ baseuri }}" @@ -168,7 +177,8 @@ def main(): password=dict(required=True, no_log=True), manager_attribute_name=dict(default='null'), manager_attribute_value=dict(default='null'), - timeout=dict(type='int', default=10) + timeout=dict(type='int', default=10), + resource_id=dict() ), supports_check_mode=False ) @@ -187,9 +197,13 @@ def main(): mgr_attributes = {'mgr_attr_name': module.params['manager_attribute_name'], 'mgr_attr_value': module.params['manager_attribute_value']} + # System, Manager or Chassis ID to modify + resource_id = module.params['resource_id'] + # Build root URI root_uri = "https://" + module.params['baseuri'] - rf_utils = IdracRedfishUtils(creds, root_uri, timeout, module) + rf_utils = IdracRedfishUtils(creds, root_uri, timeout, module, + resource_id=resource_id, data_modification=True) # Check that Category is valid if category not in CATEGORY_COMMANDS_ALL: diff --git a/lib/ansible/modules/remote_management/redfish/redfish_command.py b/lib/ansible/modules/remote_management/redfish/redfish_command.py index cea70da2655..97d00b5f846 100644 --- a/lib/ansible/modules/remote_management/redfish/redfish_command.py +++ b/lib/ansible/modules/remote_management/redfish/redfish_command.py @@ -113,6 +113,12 @@ options: - properties of account service to update type: dict version_added: "2.10" + resource_id: + required: false + description: + - The ID of the System, Manager or Chassis to modify + type: str + version_added: "2.10" author: "Jose Delarosa (@jose-delarosa)" ''' @@ -122,6 +128,7 @@ EXAMPLES = ''' redfish_command: category: Systems command: PowerGracefulRestart + resource_id: 437XR1138R2 baseuri: "{{ baseuri }}" username: "{{ username }}" password: "{{ password }}" @@ -130,6 +137,7 @@ EXAMPLES = ''' redfish_command: category: Systems command: SetOneTimeBoot + resource_id: 437XR1138R2 bootdevice: "{{ bootdevice }}" baseuri: "{{ baseuri }}" username: "{{ username }}" @@ -139,6 +147,7 @@ EXAMPLES = ''' redfish_command: category: Systems command: SetOneTimeBoot + resource_id: 437XR1138R2 bootdevice: "UefiTarget" uefi_target: "/0x31/0x33/0x01/0x01" baseuri: "{{ baseuri }}" @@ -149,6 +158,7 @@ EXAMPLES = ''' redfish_command: category: Systems command: SetOneTimeBoot + resource_id: 437XR1138R2 bootdevice: "UefiBootNext" boot_next: "Boot0001" baseuri: "{{ baseuri }}" @@ -159,6 +169,7 @@ EXAMPLES = ''' redfish_command: category: Chassis command: IndicatorLedBlink + resource_id: 1U baseuri: "{{ baseuri }}" username: "{{ username }}" password: "{{ password }}" @@ -278,6 +289,7 @@ EXAMPLES = ''' redfish_command: category: Manager command: ClearLogs + resource_id: BMC baseuri: "{{ baseuri }}" username: "{{ username }}" password: "{{ password }}" @@ -327,7 +339,8 @@ def main(): bootdevice=dict(), timeout=dict(type='int', default=10), uefi_target=dict(), - boot_next=dict() + boot_next=dict(), + resource_id=dict() ), supports_check_mode=False ) @@ -350,9 +363,13 @@ def main(): # timeout timeout = module.params['timeout'] + # System, Manager or Chassis ID to modify + resource_id = module.params['resource_id'] + # Build root URI root_uri = "https://" + module.params['baseuri'] - rf_utils = RedfishUtils(creds, root_uri, timeout, module) + rf_utils = RedfishUtils(creds, root_uri, timeout, module, + resource_id=resource_id, data_modification=True) # Check that Category is valid if category not in CATEGORY_COMMANDS_ALL: diff --git a/lib/ansible/modules/remote_management/redfish/redfish_config.py b/lib/ansible/modules/remote_management/redfish/redfish_config.py index 4eaf79b7f46..165741196a7 100644 --- a/lib/ansible/modules/remote_management/redfish/redfish_config.py +++ b/lib/ansible/modules/remote_management/redfish/redfish_config.py @@ -88,6 +88,12 @@ options: - setting dict of manager services to update type: dict version_added: "2.10" + resource_id: + required: false + description: + - The ID of the System, Manager or Chassis to modify + type: str + version_added: "2.10" author: "Jose Delarosa (@jose-delarosa)" ''' @@ -97,6 +103,7 @@ EXAMPLES = ''' redfish_config: category: Systems command: SetBiosAttributes + resource_id: 437XR1138R2 bios_attributes: BootMode: "Uefi" baseuri: "{{ baseuri }}" @@ -107,6 +114,7 @@ EXAMPLES = ''' redfish_config: category: Systems command: SetBiosAttributes + resource_id: 437XR1138R2 bios_attributes: BootMode: "Bios" OneTimeBootMode: "Enabled" @@ -119,6 +127,7 @@ EXAMPLES = ''' redfish_config: category: Systems command: SetBiosAttributes + resource_id: 437XR1138R2 bios_attribute_name: PxeDev1EnDis bios_attribute_value: Enabled baseuri: "{{ baseuri }}" @@ -129,6 +138,7 @@ EXAMPLES = ''' redfish_config: category: Systems command: SetBiosDefaultSettings + resource_id: 437XR1138R2 baseuri: "{{ baseuri }}" username: "{{ username }}" password: "{{ password }}" @@ -210,7 +220,8 @@ def main(): network_protocols=dict( type='dict', default={} - ) + ), + resource_id=dict() ), supports_check_mode=False ) @@ -237,9 +248,13 @@ def main(): # boot order boot_order = module.params['boot_order'] + # System, Manager or Chassis ID to modify + resource_id = module.params['resource_id'] + # Build root URI root_uri = "https://" + module.params['baseuri'] - rf_utils = RedfishUtils(creds, root_uri, timeout, module) + rf_utils = RedfishUtils(creds, root_uri, timeout, module, + resource_id=resource_id, data_modification=True) # Check that Category is valid if category not in CATEGORY_COMMANDS_ALL: