Server IP : 85.214.239.14 / Your IP : 18.189.182.31 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 EMC OpenManage Ansible Modules # Version 5.2.0 # Copyright (C) 2021-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 = r''' --- module: ome_profile short_description: Create, modify, delete, assign, unassign and migrate a profile on OpenManage Enterprise version_added: "3.1.0" description: "This module allows to create, modify, delete, assign, unassign, and migrate a profile on OpenManage Enterprise." extends_documentation_fragment: - dellemc.openmanage.ome_auth_options options: command: description: - C(create) creates new profiles. - "C(modify) modifies an existing profile. Only I(name), I(description), I(boot_to_network_iso), and I(attributes) can be modified." - C(delete) deletes an existing profile. - C(assign) Deploys an existing profile on a target device and returns a task ID. - C(unassign) unassigns a profile from a specified target and returns a task ID. - C(migrate) migrates an existing profile and returns a task ID. choices: [create, modify, delete, assign, unassign, migrate] default: create type: str name_prefix: description: - The name provided when creating a profile is used a prefix followed by the number assigned to it by OpenManage Enterprise. - This is applicable only for a create operation. - This option is mutually exclusive with I(name). type: str default: Profile name: description: - Name of the profile. - This is applicable for modify, delete, assign, unassign, and migrate operations. - This option is mutually exclusive with I(name_prefix) and I(number_of_profiles). type: str new_name: description: - New name of the profile. - Applicable when I(command) is C(modify). type: str number_of_profiles: description: - Provide the number of profiles to be created. - This is applicable when I(name_prefix) is used with C(create). - This option is mutually exclusive with I(name). - Openmanage Enterprise can create a maximum of 100 profiles. type: int default: 1 template_name: description: - Name of the template for creating the profile(s). - This is applicable when I(command) is C(create). - This option is mutually exclusive with I(template_id). type: str template_id: description: - ID of the template. - This is applicable when I(command) is C(create). - This option is mutually exclusive with I(template_name). type: int device_id: description: - ID of the target device. - This is applicable when I(command) is C(assign) and C(migrate). - This option is mutually exclusive with I(device_service_tag). type: int device_service_tag: description: - Identifier of the target device. - This is typically 7 to 8 characters in length. - Applicable when I(command) is C(assign), and C(migrate). - This option is mutually exclusive with I(device_id). - If the device does not exist when I(command) is C(assign) then the profile is auto-deployed. type: str description: description: Description of the profile. type: str boot_to_network_iso: description: - Details of the Share iso. - Applicable when I(command) is C(create), C(assign), and C(modify). type: dict suboptions: boot_to_network: description: Enable or disable a network share. type: bool required: true share_type: description: Type of network share. type: str choices: [NFS, CIFS] share_ip: description: IP address of the network share. type: str share_user: description: User name when I(share_type) is C(CIFS). type: str share_password: description: User password when I(share_type) is C(CIFS). type: str workgroup: description: User workgroup when I(share_type) is C(CIFS). type: str iso_path: description: Specify the full ISO path including the share name. type: str iso_timeout: description: Set the number of hours that the network ISO file will remain mapped to the target device(s). type: int choices: [1, 2, 4, 8, 16] default: 4 filters: description: - Filters the profiles based on selected criteria. - This is applicable when I(command) is C(delete) or C(unassign). - This supports suboption I(ProfileIds) which takes a list of profile IDs. - This also supports OData filter expressions with the suboption I(Filters). - See OpenManage Enterprise REST API guide for the filtering options available. - I(WARNING) When this option is used in case of C(unassign), task ID is not returned for any of the profiles affected. type: dict force: description: - Provides the option to force the migration of a profile even if the source device cannot be contacted. - This option is applicable when I(command) is C(migrate). type: bool default: false attributes: description: Attributes for C(modify) and C(assign). type: dict suboptions: Attributes: description: - List of attributes to be modified, when I(command) is C(modify). - List of attributes to be overridden when I(command) is C(assign). - "Use the I(Id) If the attribute Id is available. If not, use the comma separated I (DisplayName). For more details about using the I(DisplayName), see the example provided." type: list elements: dict Options: description: - Provides the different shut down options. - This is applicable when I(command) is C(assign). type: dict Schedule: description: - Schedule for profile deployment. - This is applicable when I(command) is C(assign). type: dict requirements: - "python >= 3.8.6" author: "Jagadeesh N V (@jagadeeshnv)" notes: - Run this module from a system that has direct access to DellEMC OpenManage Enterprise. - This module supports C(check_mode). - C(assign) operation on a already assigned profile will not redeploy. ''' EXAMPLES = r''' --- - name: Create two profiles from a template dellemc.openmanage.ome_profile: hostname: "192.168.0.1" username: "username" password: "password" ca_path: "/path/to/ca_cert.pem" template_name: "template 1" name_prefix: "omam_profile" number_of_profiles: 2 - name: Create profile with NFS share dellemc.openmanage.ome_profile: hostname: "192.168.0.1" username: "username" password: "password" ca_path: "/path/to/ca_cert.pem" command: create template_name: "template 1" name_prefix: "omam_profile" number_of_profiles: 1 boot_to_network_iso: boot_to_network: True share_type: NFS share_ip: "192.168.0.1" iso_path: "path/to/my_iso.iso" iso_timeout: 8 - name: Create profile with CIFS share dellemc.openmanage.ome_profile: hostname: "192.168.0.1" username: "username" password: "password" ca_path: "/path/to/ca_cert.pem" command: create template_name: "template 1" name_prefix: "omam_profile" number_of_profiles: 1 boot_to_network_iso: boot_to_network: True share_type: CIFS share_ip: "192.168.0.2" share_user: "username" share_password: "password" workgroup: "workgroup" iso_path: "\\path\\to\\my_iso.iso" iso_timeout: 8 - name: Modify profile name with NFS share and attributes dellemc.openmanage.ome_profile: hostname: "192.168.0.1" username: "username" password: "password" ca_path: "/path/to/ca_cert.pem" command: modify name: "Profile 00001" new_name: "modified profile" description: "new description" boot_to_network_iso: boot_to_network: True share_type: NFS share_ip: "192.168.0.3" iso_path: "path/to/my_iso.iso" iso_timeout: 8 attributes: Attributes: - Id: 4506 Value: "server attr 1" IsIgnored: false - Id: 4507 Value: "server attr 2" IsIgnored: false # Enter the comma separated string as appearing in the Detailed view on GUI # System -> Server Topology -> ServerTopology 1 Aisle Name - DisplayName: 'System, Server Topology, ServerTopology 1 Aisle Name' Value: Aisle 5 IsIgnored: false - name: Delete a profile using profile name dellemc.openmanage.ome_profile: hostname: "192.168.0.1" username: "username" password: "password" ca_path: "/path/to/ca_cert.pem" command: "delete" name: "Profile 00001" - name: Delete profiles using filters dellemc.openmanage.ome_profile: hostname: "192.168.0.1" username: "username" password: "password" ca_path: "/path/to/ca_cert.pem" command: "delete" filters: SelectAll: True Filters: =contains(ProfileName,'Profile 00002') - name: Delete profiles using profile list filter dellemc.openmanage.ome_profile: hostname: "192.168.0.1" username: "username" password: "password" ca_path: "/path/to/ca_cert.pem" command: "delete" filters: ProfileIds: - 17123 - 16124 - name: Assign a profile to target along with network share dellemc.openmanage.ome_profile: hostname: "192.168.0.1" username: "username" password: "password" ca_path: "/path/to/ca_cert.pem" command: assign name: "Profile 00001" device_id: 12456 boot_to_network_iso: boot_to_network: True share_type: NFS share_ip: "192.168.0.1" iso_path: "path/to/my_iso.iso" iso_timeout: 8 attributes: Attributes: - Id: 4506 Value: "server attr 1" IsIgnored: true Options: ShutdownType: 0 TimeToWaitBeforeShutdown: 300 EndHostPowerState: 1 StrictCheckingVlan: True Schedule: RunNow: True RunLater: False - name: Unassign a profile using profile name dellemc.openmanage.ome_profile: hostname: "192.168.0.1" username: "username" password: "password" ca_path: "/path/to/ca_cert.pem" command: "unassign" name: "Profile 00003" - name: Unassign profiles using filters dellemc.openmanage.ome_profile: hostname: "192.168.0.1" username: "username" password: "password" ca_path: "/path/to/ca_cert.pem" command: "unassign" filters: SelectAll: True Filters: =contains(ProfileName,'Profile 00003') - name: Unassign profiles using profile list filter dellemc.openmanage.ome_profile: hostname: "192.168.0.1" username: "username" password: "password" ca_path: "/path/to/ca_cert.pem" command: "unassign" filters: ProfileIds: - 17123 - 16123 - name: Migrate a profile dellemc.openmanage.ome_profile: hostname: "192.168.0.1" username: "username" password: "password" ca_path: "/path/to/ca_cert.pem" command: "migrate" name: "Profile 00001" device_id: 12456 ''' RETURN = r''' --- msg: description: Overall status of the profile operation. returned: always type: str sample: "Successfully created 2 profile(s)." profile_ids: description: IDs of the profiles created. returned: when I(command) is C(create) type: list sample: [1234, 5678] job_id: description: - Task ID created when I(command) is C(assign), C(migrate) or C(unassign). - C(assign) and C(unassign) operations do not trigger a task if a profile is auto-deployed. returned: when I(command) is C(assign), C(migrate) or C(unassign) type: int sample: 14123 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": "GEN1234", "RelatedProperties": [], "Message": "Unable to process the request because an error occurred.", "MessageArgs": [], "Severity": "Critical", "Resolution": "Retry the operation. If the issue persists, contact your system administrator." } ] } } ''' import json import time from ssl import SSLError from ansible.module_utils.basic import AnsibleModule from ansible_collections.dellemc.openmanage.plugins.module_utils.ome import RestOME, ome_auth_params from ansible.module_utils.six.moves.urllib.error import URLError, HTTPError from ansible.module_utils.urls import ConnectionError, SSLValidationError from ansible.module_utils.common.dict_transformations import recursive_diff PROFILE_VIEW = "ProfileService/Profiles" TEMPLATE_VIEW = "TemplateService/Templates" DEVICE_VIEW = "DeviceService/Devices" JOB_URI = "JobService/Jobs({job_id})" PROFILE_ACTION = "ProfileService/Actions/ProfileService.{action}" PROFILE_ATTRIBUTES = "ProfileService/Profiles({profile_id})/AttributeDetails" PROFILE_NOT_FOUND = "Profile with the name '{name}' not found." CHANGES_MSG = "Changes found to be applied." NO_CHANGES_MSG = "No changes found to be applied." SEPRTR = ',' def get_template_details(module, rest_obj): id = module.params.get('template_id') query_param = {"$filter": "Id eq {0}".format(id)} srch = 'Id' if not id: id = module.params.get('template_name') query_param = {"$filter": "Name eq '{0}'".format(id)} srch = 'Name' resp = rest_obj.invoke_request('GET', TEMPLATE_VIEW, query_param=query_param) if resp.success and resp.json_data.get('value'): tlist = resp.json_data.get('value', []) for xtype in tlist: if xtype.get(srch) == id: return xtype module.fail_json(msg="Template with {0} '{1}' not found.".format(srch, id)) def get_target_details(module, rest_obj): id = module.params.get('device_id') query_param = {"$filter": "Id eq {0}".format(id)} srch = 'Id' if not id: id = module.params.get('device_service_tag') query_param = {"$filter": "Identifier eq '{0}'".format(id)} srch = 'Identifier' resp = rest_obj.invoke_request('GET', DEVICE_VIEW, query_param=query_param) if resp.success and resp.json_data.get('value'): tlist = resp.json_data.get('value', []) for xtype in tlist: if xtype.get(srch) == id: return xtype return "Target with {0} '{1}' not found.".format(srch, id) def get_profile(rest_obj, module): """Get profile id based on requested profile name.""" profile_name = module.params["name"] profile = None query_param = {"$filter": "ProfileName eq '{0}'".format(profile_name)} profile_req = rest_obj.invoke_request("GET", PROFILE_VIEW, query_param=query_param) for each in profile_req.json_data.get('value'): if each['ProfileName'] == profile_name: profile = each break return profile def get_network_iso_payload(module): boot_iso_dict = module.params.get("boot_to_network_iso") iso_payload = {} if boot_iso_dict: iso_payload = {"BootToNetwork": False} if boot_iso_dict.get("boot_to_network"): iso_payload["BootToNetwork"] = True share_type = boot_iso_dict.get("share_type") iso_payload["ShareType"] = share_type share_detail = {} sh_ip = boot_iso_dict.get("share_ip") share_detail["IpAddress"] = sh_ip share_detail["ShareName"] = sh_ip # share_detail["ShareName"] = boot_iso_dict.get("share_name") if boot_iso_dict.get("share_name") else sh_ip share_detail["User"] = boot_iso_dict.get("share_user") share_detail["Password"] = boot_iso_dict.get("share_password") share_detail["WorkGroup"] = boot_iso_dict.get("workgroup") iso_payload["ShareDetail"] = share_detail if str(boot_iso_dict.get("iso_path")).lower().endswith('.iso'): iso_payload["IsoPath"] = boot_iso_dict.get("iso_path") else: module.fail_json(msg="ISO path does not have extension '.iso'") iso_payload["IsoTimeout"] = boot_iso_dict.get("iso_timeout") return iso_payload def recurse_subattr_list(subgroup, prefix, attr_detailed, attr_map, adv_list): if isinstance(subgroup, list): for each_sub in subgroup: nprfx = "{0}{1}{2}".format(prefix, SEPRTR, each_sub.get("DisplayName")) if each_sub.get("SubAttributeGroups"): recurse_subattr_list(each_sub.get("SubAttributeGroups"), nprfx, attr_detailed, attr_map, adv_list) else: for attr in each_sub.get('Attributes'): attr['prefix'] = nprfx # case sensitive, remove whitespaces for optim constr = "{0}{1}{2}".format(nprfx, SEPRTR, attr['DisplayName']) if constr in adv_list: attr_detailed[constr] = attr['AttributeId'] attr_map[attr['AttributeId']] = attr def get_subattr_all(attr_dtls, adv_list): attr_detailed = {} attr_map = {} for each in attr_dtls: recurse_subattr_list(each.get('SubAttributeGroups'), each.get('DisplayName'), attr_detailed, attr_map, adv_list) return attr_detailed, attr_map def attributes_check(module, rest_obj, inp_attr, profile_id): diff = 0 try: resp = rest_obj.invoke_request("GET", PROFILE_ATTRIBUTES.format(profile_id=profile_id)) attr_dtls = resp.json_data disp_adv_list = inp_attr.get("Attributes", {}) adv_list = [] for attr in disp_adv_list: if attr.get("DisplayName"): split_k = str(attr.get("DisplayName")).split(SEPRTR) trimmed = map(str.strip, split_k) n_k = SEPRTR.join(trimmed) adv_list.append(n_k) attr_detailed, attr_map = get_subattr_all(attr_dtls.get('AttributeGroups'), adv_list) payload_attr = inp_attr.get("Attributes", []) rem_attrs = [] for attr in payload_attr: if attr.get("DisplayName"): split_k = str(attr.get("DisplayName")).split(SEPRTR) trimmed = map(str.strip, split_k) n_k = SEPRTR.join(trimmed) id = attr_detailed.get(n_k, "") attr['Id'] = id attr.pop("DisplayName", None) else: id = attr.get('Id') if id: ex_val = attr_map.get(id, {}) if not ex_val: rem_attrs.append(attr) continue if attr.get('Value') != ex_val.get("Value") or attr.get('IsIgnored') != ex_val.get("IsIgnored"): diff = diff + 1 for rem in rem_attrs: payload_attr.remove(rem) # module.exit_json(attr_detailed=attr_detailed, inp_attr=disp_adv_list, payload_attr=payload_attr, adv_list=adv_list) except Exception: diff = 1 return diff def assign_profile(module, rest_obj): mparam = module.params payload = {} if mparam.get('name'): prof = get_profile(rest_obj, module) if prof: payload['Id'] = prof['Id'] else: module.fail_json(msg=PROFILE_NOT_FOUND.format(name=mparam.get('name'))) target = get_target_details(module, rest_obj) if isinstance(target, dict): payload['TargetId'] = target['Id'] if prof['ProfileState'] == 4: if prof['TargetId'] == target['Id']: module.exit_json(msg="The profile is assigned to the target {0}.".format(target['Id'])) else: module.fail_json(msg="The profile is assigned to a different target. Use the migrate command or " "unassign the profile and then proceed with assigning the profile to the target.") action = "AssignProfile" msg = "Successfully applied the assign operation." try: resp = rest_obj.invoke_request('POST', PROFILE_ACTION.format(action='GetInvalidTargetsForAssignProfile'), data={'Id': prof['Id']}) if target['Id'] in list(resp.json_data): module.fail_json(msg="The target device is invalid for the given profile.") except HTTPError: resp = None ad_opts_list = ['Attributes', 'Options', 'Schedule'] else: if mparam.get('device_id'): module.fail_json(msg=target) action = "AssignProfileForAutoDeploy" msg = "Successfully applied the assign operation for auto-deployment." payload['Identifier'] = mparam.get('device_service_tag') if prof['ProfileState'] == 1: if prof['TargetName'] == payload['Identifier']: module.exit_json(msg="The profile is assigned to the target {0}.".format(payload['Identifier'])) else: module.fail_json(msg="The profile is assigned to a different target. " "Unassign the profile and then proceed with assigning the profile to the target.") ad_opts_list = ['Attributes'] boot_iso_dict = get_network_iso_payload(module) if boot_iso_dict: payload["NetworkBootToIso"] = boot_iso_dict ad_opts = mparam.get("attributes") for opt in ad_opts_list: if ad_opts and ad_opts.get(opt): diff = attributes_check(module, rest_obj, ad_opts, prof['Id']) payload[opt] = ad_opts.get(opt) if module.check_mode: module.exit_json(msg=CHANGES_MSG, changed=True) resp = rest_obj.invoke_request('POST', PROFILE_ACTION.format(action=action), data=payload) res_dict = {'msg': msg, 'changed': True} if action == 'AssignProfile': try: res_prof = get_profile(rest_obj, module) time.sleep(5) if res_prof.get('DeploymentTaskId'): res_dict['job_id'] = res_prof.get('DeploymentTaskId') res_dict['msg'] = "Successfully triggered the job for the assign operation." except HTTPError: res_dict['msg'] = "Successfully applied the assign operation. Failed to fetch job details." module.exit_json(**res_dict) def unassign_profile(module, rest_obj): mparam = module.params prof = {} if mparam.get('name'): payload = {} prof = get_profile(rest_obj, module) if prof: if prof['ProfileState'] == 0: module.exit_json(msg="Profile is in an unassigned state.") if prof['DeploymentTaskId']: try: resp = rest_obj.invoke_request('GET', JOB_URI.format(job_id=prof['DeploymentTaskId'])) job_dict = resp.json_data job_status = job_dict.get('LastRunStatus') if job_status.get('Name') == 'Running': module.fail_json(msg="Profile deployment task is in progress. Wait for the job to finish.") except HTTPError: msg = "Unable to fetch job details. Applied the unassign operation" payload['ProfileIds'] = [prof['Id']] else: module.fail_json(msg=PROFILE_NOT_FOUND.format(name=mparam.get('name'))) if mparam.get('filters'): payload = mparam.get('filters') if module.check_mode: module.exit_json(msg=CHANGES_MSG, changed=True) msg = "Successfully applied the unassign operation. No job was triggered." resp = rest_obj.invoke_request('POST', PROFILE_ACTION.format(action='UnassignProfiles'), data=payload) res_dict = {'msg': msg, 'changed': True} try: res_prof = get_profile(rest_obj, module) time.sleep(3) if res_prof.get('DeploymentTaskId'): res_dict['job_id'] = res_prof.get('DeploymentTaskId') res_dict['msg'] = "Successfully triggered a job for the unassign operation." except HTTPError: res_dict['msg'] = "Successfully triggered a job for the unassign operation. Failed to fetch the job details." module.exit_json(**res_dict) def create_profile(module, rest_obj): mparam = module.params payload = {} template = get_template_details(module, rest_obj) payload["TemplateId"] = template["Id"] payload["NamePrefix"] = mparam.get("name_prefix") payload["NumberOfProfilesToCreate"] = mparam["number_of_profiles"] if mparam.get("description"): payload["Description"] = mparam["description"] boot_iso_dict = get_network_iso_payload(module) if boot_iso_dict: payload["NetworkBootToIso"] = boot_iso_dict if module.check_mode: module.exit_json(msg=CHANGES_MSG, changed=True) resp = rest_obj.invoke_request('POST', PROFILE_VIEW, data=payload) profile_id_list = resp.json_data module.exit_json(msg="Successfully created {0} profile(s).".format(len(profile_id_list)), changed=True, profile_ids=profile_id_list) def modify_profile(module, rest_obj): mparam = module.params payload = {} prof = get_profile(rest_obj, module) if not prof: module.fail_json(msg=PROFILE_NOT_FOUND.format(name=mparam.get('name'))) diff = 0 new_name = mparam.get('new_name') payload['Name'] = new_name if new_name else prof['ProfileName'] if new_name and new_name != prof['ProfileName']: diff += 1 desc = mparam.get('description') if desc and desc != prof['ProfileDescription']: payload['Description'] = desc diff += 1 boot_iso_dict = get_network_iso_payload(module) rdict = prof.get('NetworkBootToIso') if prof.get('NetworkBootToIso') else {} if boot_iso_dict: nest_diff = recursive_diff(boot_iso_dict, rdict) if nest_diff: # module.warn(json.dumps(nest_diff)) if nest_diff[0]: diff += 1 payload["NetworkBootToIso"] = boot_iso_dict ad_opts = mparam.get("attributes") if ad_opts and ad_opts.get("Attributes"): diff = diff + attributes_check(module, rest_obj, ad_opts, prof['Id']) if ad_opts.get("Attributes"): payload["Attributes"] = ad_opts.get("Attributes") payload['Id'] = prof['Id'] if diff: if module.check_mode: module.exit_json(msg=CHANGES_MSG, changed=True) resp = rest_obj.invoke_request('PUT', PROFILE_VIEW + "({0})".format(payload['Id']), data=payload) module.exit_json(msg="Successfully modified the profile.", changed=True) module.exit_json(msg=NO_CHANGES_MSG) def delete_profile(module, rest_obj): mparam = module.params if mparam.get('name'): prof = get_profile(rest_obj, module) if prof: if prof['ProfileState'] > 0: module.fail_json(msg="Profile has to be in an unassigned state for it to be deleted.") if module.check_mode: module.exit_json(msg=CHANGES_MSG, changed=True) resp = rest_obj.invoke_request('DELETE', PROFILE_VIEW + "({0})".format(prof['Id'])) module.exit_json(msg="Successfully deleted the profile.", changed=True) else: module.exit_json(msg=PROFILE_NOT_FOUND.format(name=mparam.get('name'))) if mparam.get('filters'): payload = mparam.get('filters') if module.check_mode: module.exit_json(msg=CHANGES_MSG, changed=True) resp = rest_obj.invoke_request('POST', PROFILE_ACTION.format(action='Delete'), data=payload) module.exit_json(msg="Successfully completed the delete operation.", changed=True) def migrate_profile(module, rest_obj): mparam = module.params payload = {} payload['ForceMigrate'] = mparam.get('force') target = get_target_details(module, rest_obj) if not isinstance(target, dict): module.fail_json(msg=target) payload['TargetId'] = target['Id'] prof = get_profile(rest_obj, module) if prof: if target['Id'] == prof['TargetId']: module.exit_json(msg=NO_CHANGES_MSG) try: resp = rest_obj.invoke_request('POST', PROFILE_ACTION.format(action='GetInvalidTargetsForAssignProfile'), data={'Id': prof['Id']}) if target['Id'] in list(resp.json_data): module.fail_json(msg="The target device is invalid for the given profile.") except HTTPError: resp = None if prof['ProfileState'] == 4: # migrate applicable in deployed state only payload['ProfileId'] = prof['Id'] if module.check_mode: module.exit_json(msg=CHANGES_MSG, changed=True) resp = rest_obj.invoke_request('POST', PROFILE_ACTION.format(action='MigrateProfile'), data=payload) msg = "Successfully applied the migrate operation." res_dict = {'msg': msg, 'changed': True} try: time.sleep(5) res_prof = get_profile(rest_obj, module) if res_prof.get('DeploymentTaskId'): res_dict['job_id'] = res_prof.get('DeploymentTaskId') res_dict['msg'] = "Successfully triggered the job for the migrate operation." except HTTPError: res_dict['msg'] = "Successfully applied the migrate operation. Failed to fetch job details." module.exit_json(**res_dict) else: module.fail_json(msg="Profile needs to be in a deployed state for a migrate operation.") else: module.fail_json(msg=PROFILE_NOT_FOUND.format(name=mparam.get('name'))) def profile_operation(module, rest_obj): command = module.params.get("command") if command == "create": create_profile(module, rest_obj) if command == "modify": modify_profile(module, rest_obj) if command == "delete": delete_profile(module, rest_obj) if command == "assign": assign_profile(module, rest_obj) if command == "unassign": unassign_profile(module, rest_obj) if command == "migrate": migrate_profile(module, rest_obj) def main(): network_iso_spec = {"boot_to_network": {"required": True, "type": 'bool'}, "share_type": {"choices": ['NFS', 'CIFS']}, "share_ip": {"type": 'str'}, "share_user": {"type": 'str'}, "share_password": {"type": 'str', "no_log": True}, "workgroup": {"type": 'str'}, "iso_path": {"type": 'str'}, "iso_timeout": {"type": 'int', "default": 4, "choices": [1, 2, 4, 8, 16]}} assign_spec = {"Attributes": {"type": 'list', "elements": 'dict'}, "Options": {"type": 'dict'}, "Schedule": {"type": 'dict'}} specs = { "command": {"default": "create", "choices": ['create', 'modify', 'delete', 'assign', 'unassign', 'migrate']}, "name_prefix": {"default": "Profile", "type": 'str'}, "name": {"type": 'str'}, "new_name": {"type": 'str'}, "number_of_profiles": {"default": 1, "type": 'int'}, "template_name": {"type": 'str'}, "template_id": {"type": "int"}, "device_id": {"type": 'int'}, "device_service_tag": {"type": 'str'}, "description": {"type": 'str'}, "boot_to_network_iso": {"type": 'dict', "options": network_iso_spec, "required_if": [ ['boot_to_network', True, ['share_type', 'share_ip', 'iso_path']], ['share_type', 'CIFS', ['share_user', 'share_password']] ]}, "filters": {"type": 'dict'}, "attributes": {"type": 'dict', "options": assign_spec}, "force": {"default": False, "type": 'bool'} } specs.update(ome_auth_params) module = AnsibleModule( argument_spec=specs, required_if=[ ['command', 'create', ['template_name', 'template_id'], True], ['command', 'modify', ['name']], ['command', 'modify', ['new_name', 'description', 'attributes', 'boot_to_network_iso'], True], ['command', 'assign', ['name']], ['command', 'assign', ['device_id', 'device_service_tag'], True], ['command', 'unassign', ['name', "filters"], True], ['command', 'delete', ['name', "filters"], True], ['command', 'migrate', ['name']], ['command', 'migrate', ['device_id', 'device_service_tag'], True], ], mutually_exclusive=[ ['name', 'name_prefix'], ['name', 'number_of_profiles'], ['name', 'filters'], ['device_id', 'device_service_tag'], ['template_name', 'template_id']], supports_check_mode=True) try: with RestOME(module.params, req_session=True) as rest_obj: profile_operation(module, rest_obj) 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, TypeError, SSLError, ConnectionError, SSLValidationError, OSError) as err: module.fail_json(msg=str(err)) if __name__ == '__main__': main()