1#!/usr/local/bin/python3.8 2# -*- coding: utf-8 -*- 3 4# 5# Dell EMC OpenManage Ansible Modules 6# Version 3.2.0 7# Copyright (C) 2019-2021 Dell Inc. or its subsidiaries. All Rights Reserved. 8 9# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 10# 11 12 13from __future__ import (absolute_import, division, print_function) 14__metaclass__ = type 15 16DOCUMENTATION = r''' 17--- 18module: ome_template 19short_description: Create, modify, deploy, delete, export, import and clone a template on OpenManage Enterprise 20version_added: "2.0.0" 21description: "This module creates, modifies, deploys, deletes, exports, imports and clones a template on 22OpenManage Enterprise." 23extends_documentation_fragment: 24 - dellemc.openmanage.ome_auth_options 25options: 26 command: 27 description: 28 - C(create) creates a new template. 29 - C(modify) modifies an existing template. 30 - C(deploy) creates a template-deployment job. 31 - C(delete) deletes an existing template. 32 - C(export) exports an existing template. 33 - C(import) creates a template from a specified configuration text in SCP XML format. 34 - C(clone) creates a clone of a existing template. 35 choices: [create, modify, deploy, delete, export, import, clone] 36 default: create 37 aliases: ['state'] 38 type: str 39 template_id: 40 description: 41 - ID of the existing template. 42 - This option is applicable when I(command) is C(modify), C(deploy), C(delete) and C(export). 43 - This option is mutually exclusive with I(template_name). 44 type: int 45 template_name: 46 description: 47 - Name of the existing template. 48 - This option is applicable when I(command) is C(modify), C(deploy), C(delete) and C(export). 49 - This option is mutually exclusive with I(template_id). 50 type: str 51 device_id: 52 description: 53 - >- 54 Specify the list of targeted device ID(s) when I(command) is C(deploy). When I (command) is C(create), 55 specify the ID of a single device. 56 - Either I(device_id) or I(device_service_tag) is mandatory or both can be applicable. 57 type: list 58 elements: int 59 default: [] 60 device_service_tag: 61 description: 62 - >- 63 Specify the list of targeted device service tags when I (command) is C(deploy). When I(command) is C(create), 64 specify the service tag of a single device. 65 - Either I(device_id) or I(device_service_tag) is mandatory or both can be applicable. 66 type: list 67 elements: str 68 default: [] 69 device_group_names: 70 description: 71 - Specify the list of groups when I (command) is C(deploy). 72 - Provide at least one of the mandatory options I(device_id), I(device_service_tag), or I(device_group_names). 73 type: list 74 elements: str 75 default: [] 76 template_view_type: 77 description: 78 - Select the type of view of the OME template. 79 - This is applicable when I(command) is C(create),C(clone) and C(import). 80 choices: [Deployment, Compliance, Inventory, Sample, None] 81 type: str 82 default: Deployment 83 attributes: 84 type: dict 85 description: 86 - >- 87 Payload data for the template operations. All the variables in this option are added as payload for C(create), 88 C(modify), C(deploy), C(import), and C(clone) operations. It takes the following attributes. 89 - >- 90 Attributes: List of dictionaries of attributes (if any) to be modified in the deployment template. This is 91 applicable when I(command) is C(deploy) and C(modify). 92 - >- 93 Name: Name of the template. This is mandatory when I(command) is C(create), C(import), C(clone), and 94 optional when I(command) is C(modify). 95 - >- 96 Description: Description for the template. This is applicable when I(command) is C(create) or C(modify). 97 - >- 98 Fqdds: This allows to create a template using components from a specified reference server. One or more, of the 99 following values must be specified in a comma-separated string: iDRAC, System, BIOS, NIC, LifeCycleController, 100 RAID, and EventFilters. If none of the values are specified, the default value 'All' is selected. 101 This is applicable when I (command) is C(create). 102 - >- 103 Options: Options to control device shutdown or end power state post template deployment. This is applicable 104 for C(deploy) operation. 105 - >- 106 Schedule: Provides options to schedule the deployment task immediately, or at a specified time. This is 107 applicable when I(command) is C(deploy). 108 - >- 109 NetworkBootIsoModel: Payload to specify the ISO deployment details. This is applicable when I(command) is 110 C(deploy). 111 - >- 112 Content: The XML content of template. This is applicable when I(command) is C(import). 113 - >- 114 Type: Template type ID, indicating the type of device for which configuration is supported, such as chassis 115 and servers. This is applicable when I(command) is C(import). 116 - >- 117 TypeId: Template type ID, indicating the type of device for which configuration is supported, such as chassis 118 and servers. This is applicable when I(command) is C(create). 119 - >- 120 Refer OpenManage Enterprise API Reference Guide for more details. 121requirements: 122 - "python >= 2.7.5" 123author: "Jagadeesh N V (@jagadeeshnv)" 124notes: 125 - Run this module from a system that has direct access to DellEMC OpenManage Enterprise. 126 - This module does not support C(check_mode). 127''' 128 129EXAMPLES = r''' 130--- 131- name: Create a template from a reference device 132 dellemc.openmanage.ome_template: 133 hostname: "192.168.0.1" 134 username: "username" 135 password: "password" 136 device_id: 25123 137 attributes: 138 Name: "New Template" 139 Description: "New Template description" 140 141- name: Modify template name, description, and attribute value 142 dellemc.openmanage.ome_template: 143 hostname: "192.168.0.1" 144 username: "username" 145 password: "password" 146 command: "modify" 147 template_id: 12 148 attributes: 149 Name: "New Custom Template" 150 Description: "Custom Template Description" 151 # Attributes to be modified in the template. 152 # For information on any attribute id, use API /TemplateService/Templates(Id)/Views(Id)/AttributeViewDetails 153 # This section is optional 154 Attributes: 155 - Id: 1234 156 Value: "Test Attribute" 157 IsIgnored: false 158 159- name: Deploy template on multiple devices 160 dellemc.openmanage.ome_template: 161 hostname: "192.168.0.1" 162 username: "username" 163 password: "password" 164 command: "deploy" 165 template_id: 12 166 device_id: 167 - 12765 168 - 10173 169 device_service_tag: 170 - 'SVTG123' 171 - 'SVTG456' 172 173- name: Deploy template on groups 174 dellemc.openmanage.ome_template: 175 hostname: "192.168.0.1" 176 username: "username" 177 password: "password" 178 command: "deploy" 179 template_id: 12 180 device_group_names: 181 - server_group_1 182 - server_group_2 183 184- name: Deploy template on multiple devices along with the attributes values to be modified on the target devices 185 dellemc.openmanage.ome_template: 186 hostname: "192.168.0.1" 187 username: "username" 188 password: "password" 189 command: "deploy" 190 template_id: 12 191 device_id: 192 - 12765 193 - 10173 194 device_service_tag: 195 - 'SVTG123' 196 attributes: 197 # Device specific attributes to be modified during deployment. 198 # For information on any attribute id, use API /TemplateService/Templates(Id)/Views(Id)/AttributeViewDetails 199 # This section is optional 200 Attributes: 201 # specific device where attribute to be modified at deployment run-time. 202 # The DeviceId should be mentioned above in the 'device_id' section. 203 # Service tags not allowed. 204 - DeviceId: 12765 205 Attributes: 206 - Id : 15645 207 Value : "0.0.0.0" 208 IsIgnored : false 209 - DeviceId: 10173 210 Attributes: 211 - Id : 18968, 212 Value : "hostname-1" 213 IsIgnored : false 214 215- name: Deploy template and Operating System (OS) on multiple devices 216 dellemc.openmanage.ome_template: 217 hostname: "192.168.0.1" 218 username: "username" 219 password: "password" 220 command: "deploy" 221 template_id: 12 222 device_id: 223 - 12765 224 device_service_tag: 225 - 'SVTG123' 226 attributes: 227 # Include this to install OS on the devices. 228 # This section is optional 229 NetworkBootIsoModel: 230 BootToNetwork: true 231 ShareType: "NFS" 232 IsoTimeout: 1 # allowable values(1,2,4,8,16) in hours 233 IsoPath: "/home/iso_path/filename.iso" 234 ShareDetail: 235 IpAddress: "192.168.0.2" 236 ShareName: "sharename" 237 User: "share_user" 238 Password: "share_password" 239 Options: 240 EndHostPowerState: 1 241 ShutdownType: 0 242 TimeToWaitBeforeShutdown: 300 243 Schedule: 244 RunLater: true 245 RunNow: false 246 247- name: "Deploy template on multiple devices and changes the device-level attributes. After the template is deployed, 248install OS using its image" 249 dellemc.openmanage.ome_template: 250 hostname: "192.168.0.1" 251 username: "username" 252 password: "password" 253 command: "deploy" 254 template_id: 12 255 device_id: 256 - 12765 257 - 10173 258 device_service_tag: 259 - 'SVTG123' 260 - 'SVTG456' 261 attributes: 262 Attributes: 263 - DeviceId: 12765 264 Attributes: 265 - Id : 15645 266 Value : "0.0.0.0" 267 IsIgnored : false 268 - DeviceId: 10173 269 Attributes: 270 - Id : 18968, 271 Value : "hostname-1" 272 IsIgnored : false 273 NetworkBootIsoModel: 274 BootToNetwork: true 275 ShareType: "NFS" 276 IsoTimeout: 1 # allowable values(1,2,4,8,16) in hours 277 IsoPath: "/home/iso_path/filename.iso" 278 ShareDetail: 279 IpAddress: "192.168.0.2" 280 ShareName: "sharename" 281 User: "share_user" 282 Password: "share_password" 283 Options: 284 EndHostPowerState: 1 285 ShutdownType: 0 286 TimeToWaitBeforeShutdown: 300 287 Schedule: 288 RunLater: true 289 RunNow: false 290 291- name: Delete template 292 dellemc.openmanage.ome_template: 293 hostname: "192.168.0.1" 294 username: "username" 295 password: "password" 296 command: "delete" 297 template_id: 12 298 299- name: Export a template 300 dellemc.openmanage.ome_template: 301 hostname: "192.168.0.1" 302 username: "username" 303 password: "password" 304 command: "export" 305 template_id: 12 306 307# Start of example to export template to a local xml file 308- name: Export template to a local xml file 309 dellemc.openmanage.ome_template: 310 hostname: "192.168.0.1" 311 username: "username" 312 password: "password" 313 command: "export" 314 template_name: "my_template" 315 register: result 316- name: Save template into a file 317 ansible.builtin.copy: 318 content: "{{ result.Content}}" 319 dest: "/path/to/exported_template.xml" 320# End of example to export template to a local xml file 321 322- name: Clone a template 323 dellemc.openmanage.ome_template: 324 hostname: "192.168.0.1" 325 username: "username" 326 password: "password" 327 command: "clone" 328 template_id: 12 329 attributes: 330 Name: "New Cloned Template Name" 331 332- name: Import template from XML content 333 dellemc.openmanage.ome_template: 334 hostname: "192.168.0.1" 335 username: "username" 336 password: "password" 337 command: "import" 338 attributes: 339 Name: "Imported Template Name" 340 # Template Type from TemplateService/TemplateTypes 341 Type: 2 342 # xml string content 343 Content: "<SystemConfiguration Model=\"PowerEdge R940\" ServiceTag=\"SVCTAG1\" 344 TimeStamp=\"Tue Sep 24 09:20:57.872551 2019\">\n<Component FQDD=\"AHCI.Slot.6-1\">\n<Attribute 345 Name=\"RAIDresetConfig\">True</Attribute>\n<Attribute Name=\"RAIDforeignConfig\">Clear</Attribute>\n 346 </Component>\n<Component FQDD=\"Disk.Direct.0-0:AHCI.Slot.6-1\">\n<Attribute Name=\"RAIDPDState\">Ready 347 </Attribute>\n<Attribute Name=\"RAIDHotSpareStatus\">No</Attribute>\n</Component>\n 348 <Component FQDD=\"Disk.Direct.1-1:AHCI.Slot.6-1\">\n<Attribute Name=\"RAIDPDState\">Ready</Attribute>\n 349 <Attribute Name=\"RAIDHotSpareStatus\">No</Attribute>\n</Component>\n</SystemConfiguration>\n" 350 351- name: Import template from local XML file 352 dellemc.openmanage.ome_template: 353 hostname: "192.168.0.1" 354 username: "username" 355 password: "password" 356 command: "import" 357 attributes: 358 Name: "Imported Template Name" 359 Type: 2 360 Content: "{{ lookup('ansible.builtin.file.', '/path/to/xmlfile') }}" 361''' 362 363RETURN = r''' 364--- 365msg: 366 description: Overall status of the template operation. 367 returned: always 368 type: str 369 sample: "Successfully created a template with ID 23" 370return_id: 371 description: ID of the template for C(create), C(modify), C(import) and C(clone) or task created in case of C(deploy). 372 returned: success, when I(command) is C(create), C(modify), C(import), C(clone) and C(deploy) 373 type: int 374 sample: 12 375TemplateId: 376 description: ID of the template for C(export). 377 returned: success, when I(command) is C(export) 378 type: int 379 sample: 13 380Content: 381 description: XML content of the exported template. This content can be written to a xml file. 382 returned: success, when I(command) is C(export) 383 type: str 384 sample: "<SystemConfiguration Model=\"PowerEdge R940\" ServiceTag=\"DG22TR2\" TimeStamp=\"Tue Sep 24 09:20:57.872551 385 2019\">\n<Component FQDD=\"AHCI.Slot.6-1\">\n<Attribute Name=\"RAIDresetConfig\">True</Attribute>\n<Attribute 386 Name=\"RAIDforeignConfig\">Clear</Attribute>\n</Component>\n<Component FQDD=\"Disk.Direct.0-0:AHCI.Slot.6-1\">\n 387 <Attribute Name=\"RAIDPDState\">Ready</Attribute>\n<Attribute Name=\"RAIDHotSpareStatus\">No</Attribute>\n 388 </Component>\n<Component FQDD=\"Disk.Direct.1-1:AHCI.Slot.6-1\">\n<Attribute Name=\"RAIDPDState\">Ready 389 </Attribute>\n<Attribute Name=\"RAIDHotSpareStatus\">No</Attribute>\n</Component>\n</SystemConfiguration>\n" 390error_info: 391 description: Details of the HTTP Error. 392 returned: on HTTP error 393 type: dict 394 sample: { 395 "error": { 396 "code": "Base.1.0.GeneralError", 397 "message": "A general error has occurred. See ExtendedInfo for more information.", 398 "@Message.ExtendedInfo": [ 399 { 400 "MessageId": "GEN1234", 401 "RelatedProperties": [], 402 "Message": "Unable to process the request because an error occurred.", 403 "MessageArgs": [], 404 "Severity": "Critical", 405 "Resolution": "Retry the operation. If the issue persists, contact your system administrator." 406 } 407 ] 408 } 409 } 410''' 411 412import json 413from ssl import SSLError 414from ansible.module_utils.basic import AnsibleModule 415from ansible_collections.dellemc.openmanage.plugins.module_utils.ome import RestOME 416from ansible.module_utils.six.moves.urllib.error import URLError, HTTPError 417from ansible.module_utils.urls import ConnectionError, SSLValidationError 418 419 420TEMPLATES_URI = "TemplateService/Templates" 421TEMPLATE_PATH = "TemplateService/Templates({template_id})" 422TEMPALTE_ACTION = "TemplateService/Actions/TemplateService.{op}" 423DEVICE_URI = "DeviceService/Devices" 424GROUP_URI = "GroupService/Groups" 425 426 427def get_group_devices_all(rest_obj, uri): 428 total_items = [] 429 next_link = uri 430 while next_link: 431 resp = rest_obj.invoke_request('GET', next_link) 432 data = resp.json_data 433 total_items.extend(data.get("value", [])) 434 next_link = str(data.get('@odata.nextLink', '')).split('/api')[-1] 435 return total_items 436 437 438def get_group(rest_obj, module, group_name): 439 query_param = {"$filter": "Name eq '{0}'".format(group_name)} 440 group_req = rest_obj.invoke_request("GET", GROUP_URI, query_param=query_param) 441 for grp in group_req.json_data.get('value'): 442 if grp['Name'] == group_name: 443 return grp 444 module.fail_json(msg="Group name '{0}' is invalid. Please provide a valid group name.".format(group_name)) 445 446 447def get_group_details(rest_obj, module): 448 group_name_list = module.params.get('device_group_names') 449 device_ids = [] 450 for group_name in group_name_list: 451 group = get_group(rest_obj, module, group_name) 452 group_uri = GROUP_URI + "({0})/Devices".format(group['Id']) 453 group_device_list = get_group_devices_all(rest_obj, group_uri) 454 device_ids.extend([dev['Id'] for dev in group_device_list]) 455 return device_ids 456 457 458def get_device_ids(module, rest_obj): 459 """Getting the list of device ids filtered from the device inventory.""" 460 target_ids = [] 461 if module.params.get('device_service_tag') or module.params.get('device_id'): 462 # device_list = get_group_devices_all(rest_obj, DEVICE_URI) 463 device_list = rest_obj.get_all_report_details(DEVICE_URI)['report_list'] 464 device_tag_id_map = dict([(device.get('DeviceServiceTag'), device.get('Id')) for device in device_list]) 465 device_id = module.params.get('device_id') 466 invalid_ids = set(device_id) - set(device_tag_id_map.values()) 467 if invalid_ids: 468 fail_module(module, msg="Unable to complete the operation because the entered target device" 469 " id(s) '{0}' are invalid.".format(",".join(list(map(str, set(invalid_ids)))))) 470 target_ids.extend(device_id) 471 service_tags = module.params.get('device_service_tag') 472 invalid_tags = set(service_tags) - set(device_tag_id_map.keys()) 473 if invalid_tags: 474 fail_module(module, msg="Unable to complete the operation because the entered target service" 475 " tag(s) '{0}' are invalid.".format(",".join(set(invalid_tags)))) 476 for tag in service_tags: # append ids for service tags 477 target_ids.append(device_tag_id_map.get(tag)) 478 if module.params.get('device_group_names'): 479 target_ids.extend(get_group_details(rest_obj, module)) 480 return list(set(target_ids)) # set to eliminate duplicates 481 482 483def get_view_id(rest_obj, viewstr): 484 resp = rest_obj.invoke_request('GET', "TemplateService/TemplateViewTypes") 485 if resp.success and resp.json_data.get('value'): 486 tlist = resp.json_data.get('value', []) 487 for xtype in tlist: 488 if xtype.get('Description', "") == viewstr: 489 return xtype.get('Id') 490 viewmap = {"Deployment": 2, "Compliance": 1, "Inventory": 3, "Sample": 4, "None": 0} 491 return viewmap.get(viewstr) 492 493 494def get_type_id_valid(rest_obj, typeid): 495 resp = rest_obj.invoke_request('GET', "TemplateService/TemplateTypes") 496 if resp.success and resp.json_data.get('value'): 497 tlist = resp.json_data.get('value', []) 498 for xtype in tlist: 499 if xtype.get('Id') == typeid: # use Name if str is passed 500 return True 501 return False 502 503 504def get_create_payload(module_params, deviceid, view_id): 505 create_payload = {"Fqdds": "All", 506 "ViewTypeId": view_id} 507 if isinstance(module_params.get("attributes"), dict): 508 create_payload.update(module_params.get("attributes")) 509 create_payload["SourceDeviceId"] = int(deviceid) 510 return create_payload 511 512 513def get_modify_payload(module_params, template_id, template_dict): 514 modify_payload = {} 515 if isinstance(module_params.get("attributes"), dict): 516 modify_payload.update(module_params.get("attributes")) 517 modify_payload['Id'] = template_id 518 # Update with old template values 519 if not modify_payload.get("Name"): 520 modify_payload["Name"] = template_dict["Name"] 521 if not modify_payload.get("Description"): 522 modify_payload["Description"] = template_dict["Description"] 523 return modify_payload 524 525 526def get_deploy_payload(module_params, deviceidlist, template_id): 527 deploy_payload = {} 528 if isinstance(module_params.get("attributes"), dict): 529 deploy_payload.update(module_params.get("attributes")) 530 deploy_payload["Id"] = template_id 531 deploy_payload["TargetIds"] = deviceidlist 532 return deploy_payload 533 534 535def get_import_payload(module, rest_obj, view_id): 536 attrib_dict = module.params.get("attributes").copy() 537 import_payload = {} 538 import_payload["Name"] = attrib_dict.pop("Name") 539 import_payload["ViewTypeId"] = view_id 540 import_payload["Type"] = 2 541 typeid = attrib_dict.get("Type") 542 if typeid: 543 if get_type_id_valid(rest_obj, typeid): 544 import_payload["Type"] = typeid # Type is mandatory for import 545 else: 546 fail_module(module, msg="Type provided for 'import' operation is invalid") 547 import_payload["Content"] = attrib_dict.pop("Content") 548 if isinstance(attrib_dict, dict): 549 import_payload.update(attrib_dict) 550 return import_payload 551 552 553def get_clone_payload(module_params, template_id, view_id): 554 attrib_dict = module_params.get("attributes").copy() 555 clone_payload = {} 556 clone_payload["SourceTemplateId"] = template_id 557 clone_payload["NewTemplateName"] = attrib_dict.pop("Name") 558 clone_payload["ViewTypeId"] = view_id 559 if isinstance(attrib_dict, dict): 560 clone_payload.update(attrib_dict) 561 return clone_payload 562 563 564def get_template_by_id(module, rest_obj, template_id): 565 path = TEMPLATE_PATH.format(template_id=template_id) 566 template_req = rest_obj.invoke_request("GET", path) 567 if template_req.success: 568 return template_req.json_data 569 else: 570 fail_module(module, msg="Unable to complete the operation because the" 571 " requested template is not present.") 572 573 574def get_template_by_name(template_name, module, rest_obj): 575 """Filter out specific template based on name, and it returns template_id. 576 577 :param template_name: string 578 :param module: dictionary 579 :param rest_obj: object 580 :return: template_id: integer 581 """ 582 template_id = None 583 template = None 584 template_path = TEMPLATES_URI 585 query_param = {"$filter": "Name eq '{0}'".format(template_name)} 586 template_req = rest_obj.invoke_request("GET", template_path, query_param=query_param) 587 for each in template_req.json_data.get('value'): 588 if each['Name'] == template_name: 589 template_id = each['Id'] 590 template = each 591 break 592 else: 593 fail_module(module, msg="Unable to complete the operation because the" 594 " requested template with name {0} is not present.".format(template_name)) 595 return template, template_id 596 597 598def _get_resource_parameters(module, rest_obj): 599 command = module.params.get("command") 600 rest_method = 'POST' 601 payload = {} 602 template_id = module.params.get("template_id") 603 template_name = module.params.get("template_name") 604 if template_name: 605 template, template_id = get_template_by_name(template_name, module, rest_obj) 606 if command not in ["import", "create"] and template_id is None: 607 fail_module(module, msg="Enter a valid template_name or template_id") 608 if command == "create": 609 devid_list = get_device_ids(module, rest_obj) 610 if len(devid_list) != 1: 611 fail_module(module, msg="Create template requires only one reference device") 612 view_id = get_view_id(rest_obj, module.params['template_view_type']) 613 payload = get_create_payload(module.params, devid_list[0], view_id) 614 path = TEMPLATES_URI 615 elif command == "modify": 616 path = TEMPLATE_PATH.format(template_id=template_id) 617 template_dict = get_template_by_id(module, rest_obj, template_id) 618 payload = get_modify_payload(module.params, template_id, template_dict) 619 rest_method = 'PUT' 620 elif command == "delete": 621 path = TEMPLATE_PATH.format(template_id=template_id) 622 rest_method = 'DELETE' 623 elif command == "export": 624 path = TEMPALTE_ACTION.format(op="Export") 625 payload = {'TemplateId': template_id} 626 elif command == "deploy": 627 devid_list = get_device_ids(module, rest_obj) 628 if not devid_list: 629 fail_module(module, msg="There are no devices provided for deploy operation") 630 path = TEMPALTE_ACTION.format(op="Deploy") 631 payload = get_deploy_payload(module.params, devid_list, template_id) 632 elif command == "clone": 633 view_id = get_view_id(rest_obj, module.params['template_view_type']) 634 path = TEMPALTE_ACTION.format(op="Clone") 635 payload = get_clone_payload(module.params, template_id, view_id) 636 else: 637 view_id = get_view_id(rest_obj, module.params['template_view_type']) 638 path = TEMPALTE_ACTION.format(op="Import") 639 payload = get_import_payload(module, rest_obj, view_id) 640 return path, payload, rest_method 641 642 643def _validate_inputs(module): 644 """validates input parameters""" 645 command = module.params.get("command") 646 if command in ["create", "deploy"]: 647 dev_id = module.params["device_id"] 648 dev_st = module.params["device_service_tag"] 649 if None in dev_id or None in dev_st: 650 fail_module(module, msg="Argument device_id or device_service_tag has null values") 651 attrib_dict = {} 652 if module.params.get("attributes"): 653 attrib_dict = module.params.get("attributes") 654 if command in ["import", "clone", "create"]: 655 if not attrib_dict.get("Name"): 656 fail_module(module, msg="Argument 'Name' required in attributes for {0} operation".format(command)) 657 if command == "import": 658 if not attrib_dict.get("Content"): 659 fail_module(module, msg="Argument 'Content' required in attributes for {0} operation".format(command)) 660 661 662def password_no_log(attributes): 663 if isinstance(attributes, dict): 664 netdict = attributes.get("NetworkBootIsoModel") 665 if isinstance(netdict, dict): 666 sharedet = netdict.get("ShareDetail") 667 if isinstance(sharedet, dict) and 'Password' in sharedet: 668 sharedet['Password'] = "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER" 669 670 671def fail_module(module, **failmsg): 672 password_no_log(module.params.get("attributes")) 673 module.fail_json(**failmsg) 674 675 676def exit_module(module, response): 677 password_no_log(module.params.get("attributes")) 678 resp = None 679 my_change = True 680 command = module.params.get('command') 681 result = {} 682 if command in ["create", "modify", "deploy", "import", "clone"]: 683 result["return_id"] = response.json_data 684 resp = result["return_id"] 685 if command == 'deploy' and result["return_id"] == 0: 686 result["failed"] = True 687 command = 'deploy_fail' 688 my_change = False 689 if command == 'export': 690 my_change = False 691 result = response.json_data 692 msg_dict = {'create': "Successfully created a template with ID {0}".format(resp), 693 'modify': "Successfully modified the template with ID {0}".format(resp), 694 'deploy': "Successfully created the template-deployment job with ID {0}".format(resp), 695 'deploy_fail': 'Failed to deploy template.', 696 'delete': "Deleted successfully", 697 'export': "Exported successfully", 698 'import': "Imported successfully", 699 'clone': "Cloned successfully"} 700 module.exit_json(msg=msg_dict.get(command), changed=my_change, **result) 701 702 703def main(): 704 module = AnsibleModule( 705 argument_spec={ 706 "hostname": {"required": True, "type": 'str'}, 707 "username": {"required": True, "type": 'str'}, 708 "password": {"required": True, "type": 'str', "no_log": True}, 709 "port": {"required": False, "default": 443, "type": 'int'}, 710 "command": {"required": False, "default": "create", "aliases": ['state'], 711 "choices": ['create', 'modify', 'deploy', 'delete', 'export', 'import', 'clone']}, 712 "template_id": {"required": False, "type": 'int'}, 713 "template_name": {"required": False, "type": 'str'}, 714 "template_view_type": {"required": False, "default": 'Deployment', 715 "choices": ['Deployment', 'Compliance', 'Inventory', 'Sample', 'None']}, 716 "device_id": {"required": False, "type": 'list', "default": [], "elements": 'int'}, 717 "device_service_tag": {"required": False, "type": 'list', "default": [], "elements": 'str'}, 718 "device_group_names": {"required": False, "type": 'list', "default": [], "elements": 'str'}, 719 "attributes": {"required": False, "type": 'dict'}, 720 }, 721 required_if=[ 722 ['command', 'create', ['attributes']], 723 ['command', 'modify', ['attributes']], 724 ['command', 'import', ['attributes']], 725 ['command', 'modify', ['template_id', 'template_name'], True], 726 ['command', 'delete', ['template_id', 'template_name'], True], 727 ['command', 'export', ['template_id', 'template_name'], True], 728 ['command', 'clone', ['template_id', 'template_name'], True], 729 ['command', 'deploy', ['template_id', 'template_name'], True], 730 ['command', 'deploy', ['device_id', 'device_service_tag', 'device_group_names'], True], 731 ], 732 mutually_exclusive=[["template_id", "template_name"]], 733 supports_check_mode=False) 734 735 try: 736 _validate_inputs(module) 737 with RestOME(module.params, req_session=True) as rest_obj: 738 path, payload, rest_method = _get_resource_parameters(module, rest_obj) 739 resp = rest_obj.invoke_request(rest_method, path, data=payload) 740 if resp.success: 741 exit_module(module, resp) 742 except HTTPError as err: 743 fail_module(module, msg=str(err), error_info=json.load(err)) 744 except URLError as err: 745 password_no_log(module.params.get("attributes")) 746 module.exit_json(msg=str(err), unreachable=True) 747 except (IOError, SSLError, SSLValidationError, ConnectionError, TypeError, ValueError, KeyError) as err: 748 fail_module(module, msg=str(err)) 749 750 751if __name__ == '__main__': 752 main() 753