Server IP : 85.214.239.14 / Your IP : 3.135.218.39 Web Server : Apache/2.4.62 (Debian) System : Linux h2886529.stratoserver.net 4.9.0 #1 SMP Tue Jan 9 19:45:01 MSK 2024 x86_64 User : www-data ( 33) PHP Version : 7.4.18 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare, MySQL : OFF | cURL : OFF | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : OFF Directory : /usr/lib/python3/dist-packages/ansible_collections/dellemc/openmanage/plugins/modules/ |
Upload File : |
#!/usr/bin/python # -*- coding: utf-8 -*- # # Dell OpenManage Ansible Modules # Version 6.1.0 # Copyright (C) 2022 Dell Inc. or its subsidiaries. All Rights Reserved. # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # from __future__ import (absolute_import, division, print_function) __metaclass__ = type DOCUMENTATION = """ --- module: ome_devices short_description: Perform device-specific operations on target devices description: Perform device-specific operations such as refresh inventory, clear iDRAC job queue, and reset iDRAC from OpenManage Enterprise. version_added: 6.1.0 author: - Jagadeesh N V(@jagadeeshnv) extends_documentation_fragment: - dellemc.openmanage.oment_auth_options options: device_service_tags: description: - Service tag of the target devices. - This is mutually exclusive with I(device_ids). type: list elements: str device_ids: description: - IDs of the target devices. - This is mutually exclusive with I(device_service_tags). type: list elements: int state: description: - C(present) Allows to perform the I(device_action) on the target devices. - "C(absent) Removes the device from OpenManage Enterprise. Job is not triggered. I(job_wait), I(job_schedule), I(job_name), and I(job_description) are not applicable to this operation." type: str choices: [present, absent] default: present device_action: description: - C(refresh_inventory) refreshes the inventory on the target devices. - C(reset_idrac) Triggers a reset on the target iDRACs. - C(clear_idrac_job_queue) Clears the job queue on the target iDRACs. - A job is triggered for each action. type: str choices: [refresh_inventory, reset_idrac, clear_idrac_job_queue] default: refresh_inventory job_wait: description: - Provides an option to wait for the job completion. - This option is applicable when I(state) is C(present). - This is applicable when I(job_schedule) is C(startnow). type: bool default: true job_wait_timeout: description: - The maximum wait time of I(job_wait) in seconds. The job is tracked only for this duration. - This option is applicable when I(job_wait) is C(True). type: int default: 1200 job_schedule: description: Provide the cron string to schedule the job. type: str default: startnow job_name: description: Optional name for the job. type: str job_description: description: Optional description for the job. type: str requirements: - "python >= 3.8.6" notes: - For C(idrac_reset), the job triggers only the iDRAC reset operation and does not track the complete reset cycle. - Run this module from a system that has direct access to Dell OpenManage Enterprise. - This module supports C(check_mode). """ EXAMPLES = """ --- - name: Refresh Inventory dellemc.openmanage.ome_devices: hostname: "192.168.0.1" username: "username" password: "password" ca_path: "/path/to/ca_cert.pem" device_action: refresh_inventory device_service_tags: - SVCTAG1 - name: Clear iDRAC job queue dellemc.openmanage.ome_devices: hostname: "192.168.0.1" username: "username" password: "password" ca_path: "/path/to/ca_cert.pem" device_action: clear_idrac_job_queue device_service_tags: - SVCTAG1 - name: Reset iDRAC using the service tag dellemc.openmanage.ome_devices: hostname: "192.168.0.1" username: "username" password: "password" ca_path: "/path/to/ca_cert.pem" device_action: reset_idrac device_service_tags: - SVCTAG1 - name: Remove devices using servicetags dellemc.openmanage.ome_devices: hostname: "192.168.0.1" username: "username" password: "password" ca_path: "/path/to/ca_cert.pem" state: absent device_service_tags: - SVCTAG1 - SVCTAF2 - name: Remove devices using IDs dellemc.openmanage.ome_devices: hostname: "192.168.0.1" username: "username" password: "password" ca_path: "/path/to/ca_cert.pem" state: absent device_ids: - 10235 """ RETURN = """ --- msg: type: str description: Overall status of the devices operation. returned: always sample: "Successfully removed the device(s)." job: type: dict description: Job details of the devices operation. returned: success sample: { "Id": 14874, "JobName": "Refresh inventory", "JobDescription": "The Refresh inventory task initiated from OpenManage Ansible Modules for devices with the ids '13216'.", "Schedule": "startnow", "State": "Enabled", "CreatedBy": "admin", "UpdatedBy": null, "Visible": true, "Editable": true, "Builtin": false, "UserGenerated": true, "Targets": [ { "JobId": 14874, "Id": 13216, "Data": "", "TargetType": { "Id": 1000, "Name": "DEVICE" } } ], "Params": [ { "JobId": 14874, "Key": "action", "Value": "CONFIG_INVENTORY" }, { "JobId": 14874, "Key": "isCollectDriverInventory", "Value": "true" } ], "LastRunStatus": { "@odata.type": "#JobService.JobStatus", "Id": 2060, "Name": "Completed" }, "JobType": { "@odata.type": "#JobService.JobType", "Id": 8, "Name": "Inventory_Task", "Internal": false }, "JobStatus": { "@odata.type": "#JobService.JobStatus", "Id": 2020, "Name": "Scheduled" }, "ExecutionHistories@odata.navigationLink": "/api/JobService/Jobs(14874)/ExecutionHistories", "LastExecutionDetail": { "@odata.id": "/api/JobService/Jobs(14874)/LastExecutionDetail" } } error_info: description: Details of the HTTP Error. returned: on HTTP error type: dict sample: { "error": { "code": "Base.1.0.GeneralError", "message": "A general error has occurred. See ExtendedInfo for more information.", "@Message.ExtendedInfo": [ { "MessageId": "CGEN1002", "RelatedProperties": [], "Message": "Unable to complete the operation because the requested URI is invalid.", "MessageArgs": [], "Severity": "Critical", "Resolution": "Enter a valid URI and retry the operation." } ] } } """ import json from ssl import SSLError from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.six.moves.urllib.error import URLError, HTTPError from ansible.module_utils.urls import ConnectionError from ansible_collections.dellemc.openmanage.plugins.module_utils.ome import RestOME, ome_auth_params from ansible_collections.dellemc.openmanage.plugins.module_utils.utils import \ get_rest_items, strip_substr_dict, job_tracking, apply_diff_key from ansible_collections.dellemc.openmanage.plugins.module_utils.utils import CHANGES_MSG, NO_CHANGES_MSG DEVICE_URI = "DeviceService/Devices" JOBS_URI = "JobService/Jobs" JOB_URI = "JobService/Jobs({job_id})" RUN_JOB_URI = "JobService/Actions/JobService.RunJobs" LAST_EXEC = "JobService/Jobs({job_id})/LastExecutionDetail" DELETE_DEVICES_URI = "DeviceService/Actions/DeviceService.RemoveDevices" DELETE_SUCCESS = "The devices(s) are removed successfully." INVALID_DEV_ST = "Unable to complete the operation because the entered target device(s) '{0}' are invalid." JOB_DESC = "The {0} task initiated from OpenManage Ansible Modules for devices with the ids '{1}'." APPLY_TRIGGERED = "Successfully initiated the device action job." JOB_SCHEDULED = "The job is scheduled successfully." SUCCESS_MSG = "The device operation is performed successfully." all_device_types = [1000, 2000, 4000, 5000, 7000, 8000, 9001] device_type_map = {"refresh_inventory": all_device_types, "reset_idrac": [1000], "clear_idrac_job_queue": [1000]} job_type_map = {"refresh_inventory": 8, "reset_idrac": 3, "clear_idrac_job_queue": 3} jtype_map = {3: "DeviceAction_Task", 8: "Inventory_Task"} job_params_map = {"refresh_inventory": {"action": "CONFIG_INVENTORY", "isCollectDriverInventory": "true"}, "reset_idrac": {"operationName": "RESET_IDRAC"}, "clear_idrac_job_queue": {"operationName": "REMOTE_RACADM_EXEC", "Command": "jobqueue delete -i JID_CLEARALL_FORCE", "CommandTimeout": "60", "deviceTypes": "1000"}} jobname_map = {"refresh_inventory": "Refresh inventory", "reset_idrac": "Reset iDRAC", "clear_idrac_job_queue": "Clear iDRAC job queue"} def get_dev_ids(module, rest_obj, types): invalids = set() sts = module.params.get('device_ids') param = "{0} eq {1}" srch = 'Id' if not sts: sts = module.params.get('device_service_tags') param = "{0} eq '{1}'" srch = 'Identifier' devs = [] for st in sts: resp = rest_obj.invoke_request("GET", DEVICE_URI, query_param={"$filter": param.format(srch, st)}) val = resp.json_data.get('value') if not val: invalids.add(st) for v in val: if v[srch] == st: if v["Type"] in types: devs.extend(val) else: invalids.add(st) break else: invalids.add(st) valids = [(dv.get('Id')) for dv in devs] return valids, invalids def delete_devices(module, rest_obj, valid_ids): if module.check_mode: module.exit_json(msg=CHANGES_MSG, changed=True) payload = {"DeviceIds": list(valid_ids)} rest_obj.invoke_request('POST', DELETE_DEVICES_URI, data=payload) module.exit_json(msg=DELETE_SUCCESS, changed=True) def update_common_job(module, payload, task, valid_ids): payload["Schedule"] = module.params.get('job_schedule') if module.params.get('job_name'): payload["JobName"] = module.params.get('job_name') else: payload["JobName"] = jobname_map.get(task) if module.params.get('job_description'): payload["JobDescription"] = module.params.get('job_description') else: payload["JobDescription"] = JOB_DESC.format(jobname_map.get(task), ",".join(map(str, valid_ids))) def check_similar_job(rest_obj, payload): query_param = {"$filter": "JobType/Id eq {0}".format(payload['JobType'])} job_resp = rest_obj.invoke_request("GET", JOBS_URI, query_param=query_param) job_list = job_resp.json_data.get('value', []) for jb in job_list: if jb['JobName'] == payload['JobName'] and jb['JobDescription'] == payload['JobDescription'] and \ jb['Schedule'] == payload['Schedule']: jb_prm = dict((k.get('Key'), k.get('Value')) for k in jb.get('Params')) if not jb_prm == payload.get('Params'): continue trgts = dict((t.get('Id'), t.get('TargetType').get('Name')) for t in jb.get('Targets')) if not trgts == payload.get('Targets'): continue return jb return {} def job_wait(module, rest_obj, job): mparams = module.params if mparams.get('job_schedule') != 'startnow': module.exit_json(changed=True, msg=JOB_SCHEDULED, job=strip_substr_dict(job)) if not module.params.get("job_wait"): module.exit_json(changed=True, msg=APPLY_TRIGGERED, job=strip_substr_dict(job)) else: job_msg = SUCCESS_MSG job_failed, msg, job_dict, wait_time = job_tracking( rest_obj, JOB_URI.format(job_id=job['Id']), max_job_wait_sec=module.params.get('job_wait_timeout'), initial_wait=3) if job_failed: try: job_resp = rest_obj.invoke_request('GET', LAST_EXEC.format(job_id=job['Id'])) msg = job_resp.json_data.get("Value") job_msg = msg.replace('\n', ' ') except Exception: job_msg = msg module.exit_json(failed=job_failed, msg=job_msg, job=strip_substr_dict(job), changed=True) def get_task_payload(task): taskload = {} taskload.update({"JobType": job_type_map.get(task, 8)}) taskload.update({"Params": job_params_map.get(task, {})}) return taskload def get_payload_method(task, valid_ids): payload = get_task_payload(task) targets = dict((dv, "DEVICE") for dv in valid_ids) payload["Targets"] = targets return payload, "POST", JOBS_URI def formalize_job_payload(payload): payload["Id"] = 0 payload["State"] = "Enabled" prms = payload['Params'] payload['Params'] = [({"Key": k, "Value": v}) for k, v in prms.items()] trgts = payload['Targets'] payload['Targets'] = [({"Id": k, "Data": "", "TargetType": {"Id": 1000, "Name": v}}) for k, v in trgts.items()] jtype = payload["JobType"] payload["JobType"] = {"Id": jtype, "Name": jtype_map.get(jtype)} def perform_device_tasks(module, rest_obj, valid_ids): task = module.params.get("device_action") payload, method, uri = get_payload_method(task, valid_ids) update_common_job(module, payload, task, valid_ids) job = check_similar_job(rest_obj, payload) if not job: formalize_job_payload(payload) if module.check_mode: module.exit_json(msg=CHANGES_MSG, changed=True) resp = rest_obj.invoke_request("POST", JOBS_URI, data=payload, api_timeout=60) job_wait(module, rest_obj, resp.json_data) else: if module.params.get('job_schedule') == 'startnow' and job["LastRunStatus"]['Id'] != 2050: if module.check_mode: module.exit_json(msg=CHANGES_MSG, changed=True) resp = rest_obj.invoke_request("POST", RUN_JOB_URI, data={"JobIds": [job['Id']]}) job_wait(module, rest_obj, job) module.exit_json(msg=NO_CHANGES_MSG, job=strip_substr_dict(job)) def main(): specs = { "device_service_tags": {"type": "list", "elements": 'str'}, "device_ids": {"type": "list", "elements": 'int'}, "state": {"type": "str", "choices": ["present", "absent"], "default": "present"}, "device_action": {"type": "str", "choices": ["refresh_inventory", "reset_idrac", "clear_idrac_job_queue"], "default": 'refresh_inventory'}, "job_wait": {"type": "bool", "default": True}, "job_wait_timeout": {"type": "int", "default": 1200}, "job_schedule": {"type": "str", "default": 'startnow'}, "job_name": {"type": "str"}, "job_description": {"type": "str"}, # "job_params": {"type": "dict"} } specs.update(ome_auth_params) module = AnsibleModule( argument_spec=specs, required_if=[], mutually_exclusive=[ ("device_service_tags", "device_ids"), ], required_one_of=[("device_service_tags", "device_ids")], supports_check_mode=True ) try: with RestOME(module.params, req_session=True) as rest_obj: if module.params.get("state") == 'present': valids, invalids = get_dev_ids(module, rest_obj, device_type_map.get(module.params.get("device_action"))) if invalids: module.exit_json(failed=True, msg=INVALID_DEV_ST.format(",".join(map(str, invalids)))) perform_device_tasks(module, rest_obj, valids) else: valids, invalids = get_dev_ids(module, rest_obj, all_device_types) if not valids: module.exit_json(msg=NO_CHANGES_MSG) delete_devices(module, rest_obj, valids) except HTTPError as err: module.fail_json(msg=str(err), error_info=json.load(err)) except URLError as err: module.exit_json(msg=str(err), unreachable=True) except (IOError, ValueError, SSLError, TypeError, ConnectionError, AttributeError, IndexError, KeyError, OSError) as err: module.fail_json(msg=str(err)) if __name__ == '__main__': main()