1#!/usr/bin/python 2# -*- coding: utf-8 -*- 3 4# (c) 2013-2014, Epic Games, Inc. 5# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 6 7from __future__ import absolute_import, division, print_function 8__metaclass__ = type 9 10 11ANSIBLE_METADATA = {'metadata_version': '1.1', 12 'status': ['preview'], 13 'supported_by': 'community'} 14 15 16DOCUMENTATION = r''' 17--- 18module: zabbix_host 19short_description: Create/update/delete Zabbix hosts 20description: 21 - This module allows you to create, modify and delete Zabbix host entries and associated group and template data. 22version_added: "2.0" 23author: 24 - "Cove (@cove)" 25 - Tony Minfei Ding (!UNKNOWN) 26 - Harrison Gu (@harrisongu) 27 - Werner Dijkerman (@dj-wasabi) 28 - Eike Frost (@eikef) 29requirements: 30 - "python >= 2.6" 31 - "zabbix-api >= 0.5.4" 32options: 33 host_name: 34 description: 35 - Name of the host in Zabbix. 36 - I(host_name) is the unique identifier used and cannot be updated using this module. 37 required: true 38 type: str 39 visible_name: 40 description: 41 - Visible name of the host in Zabbix. 42 version_added: '2.3' 43 type: str 44 description: 45 description: 46 - Description of the host in Zabbix. 47 version_added: '2.5' 48 type: str 49 host_groups: 50 description: 51 - List of host groups the host is part of. 52 type: list 53 elements: str 54 link_templates: 55 description: 56 - List of templates linked to the host. 57 type: list 58 elements: str 59 inventory_mode: 60 description: 61 - Configure the inventory mode. 62 choices: ['automatic', 'manual', 'disabled'] 63 version_added: '2.1' 64 type: str 65 inventory_zabbix: 66 description: 67 - Add Facts for a zabbix inventory (e.g. Tag) (see example below). 68 - Please review the interface documentation for more information on the supported properties 69 - U(https://www.zabbix.com/documentation/3.2/manual/api/reference/host/object#host_inventory) 70 version_added: '2.5' 71 type: dict 72 status: 73 description: 74 - Monitoring status of the host. 75 choices: ['enabled', 'disabled'] 76 default: 'enabled' 77 type: str 78 state: 79 description: 80 - State of the host. 81 - On C(present), it will create if host does not exist or update the host if the associated data is different. 82 - On C(absent) will remove a host if it exists. 83 choices: ['present', 'absent'] 84 default: 'present' 85 type: str 86 proxy: 87 description: 88 - The name of the Zabbix proxy to be used. 89 type: str 90 interfaces: 91 type: list 92 elements: dict 93 description: 94 - List of interfaces to be created for the host (see example below). 95 - For more information, review host interface documentation at 96 - U(https://www.zabbix.com/documentation/4.0/manual/api/reference/hostinterface/object) 97 suboptions: 98 type: 99 description: 100 - Interface type to add 101 - Numerical values are also accepted for interface type 102 - 1 = agent 103 - 2 = snmp 104 - 3 = ipmi 105 - 4 = jmx 106 choices: ['agent', 'snmp', 'ipmi', 'jmx'] 107 required: true 108 main: 109 type: int 110 description: 111 - Whether the interface is used as default. 112 - If multiple interfaces with the same type are provided, only one can be default. 113 - 0 (not default), 1 (default) 114 default: 0 115 choices: [0, 1] 116 useip: 117 type: int 118 description: 119 - Connect to host interface with IP address instead of DNS name. 120 - 0 (don't use ip), 1 (use ip) 121 default: 0 122 choices: [0, 1] 123 ip: 124 type: str 125 description: 126 - IP address used by host interface. 127 - Required if I(useip=1). 128 default: '' 129 dns: 130 type: str 131 description: 132 - DNS name of the host interface. 133 - Required if I(useip=0). 134 default: '' 135 port: 136 type: str 137 description: 138 - Port used by host interface. 139 - If not specified, default port for each type of interface is used 140 - 10050 if I(type='agent') 141 - 161 if I(type='snmp') 142 - 623 if I(type='ipmi') 143 - 12345 if I(type='jmx') 144 bulk: 145 type: int 146 description: 147 - Whether to use bulk SNMP requests. 148 - 0 (don't use bulk requests), 1 (use bulk requests) 149 choices: [0, 1] 150 default: 1 151 default: [] 152 tls_connect: 153 description: 154 - Specifies what encryption to use for outgoing connections. 155 - Possible values, 1 (no encryption), 2 (PSK), 4 (certificate). 156 - Works only with >= Zabbix 3.0 157 default: 1 158 version_added: '2.5' 159 type: int 160 tls_accept: 161 description: 162 - Specifies what types of connections are allowed for incoming connections. 163 - The tls_accept parameter accepts values of 1 to 7 164 - Possible values, 1 (no encryption), 2 (PSK), 4 (certificate). 165 - Values can be combined. 166 - Works only with >= Zabbix 3.0 167 default: 1 168 version_added: '2.5' 169 type: int 170 tls_psk_identity: 171 description: 172 - It is a unique name by which this specific PSK is referred to by Zabbix components 173 - Do not put sensitive information in the PSK identity string, it is transmitted over the network unencrypted. 174 - Works only with >= Zabbix 3.0 175 version_added: '2.5' 176 type: str 177 tls_psk: 178 description: 179 - PSK value is a hard to guess string of hexadecimal digits. 180 - The preshared key, at least 32 hex digits. Required if either I(tls_connect) or I(tls_accept) has PSK enabled. 181 - Works only with >= Zabbix 3.0 182 version_added: '2.5' 183 type: str 184 ca_cert: 185 description: 186 - Required certificate issuer. 187 - Works only with >= Zabbix 3.0 188 version_added: '2.5' 189 aliases: [ tls_issuer ] 190 type: str 191 tls_subject: 192 description: 193 - Required certificate subject. 194 - Works only with >= Zabbix 3.0 195 version_added: '2.5' 196 type: str 197 ipmi_authtype: 198 description: 199 - IPMI authentication algorithm. 200 - Please review the Host object documentation for more information on the supported properties 201 - 'https://www.zabbix.com/documentation/3.4/manual/api/reference/host/object' 202 - Possible values are, C(0) (none), C(1) (MD2), C(2) (MD5), C(4) (straight), C(5) (OEM), C(6) (RMCP+), 203 with -1 being the API default. 204 - Please note that the Zabbix API will treat absent settings as default when updating 205 any of the I(ipmi_)-options; this means that if you attempt to set any of the four 206 options individually, the rest will be reset to default values. 207 version_added: '2.5' 208 type: int 209 ipmi_privilege: 210 description: 211 - IPMI privilege level. 212 - Please review the Host object documentation for more information on the supported properties 213 - 'https://www.zabbix.com/documentation/3.4/manual/api/reference/host/object' 214 - Possible values are C(1) (callback), C(2) (user), C(3) (operator), C(4) (admin), C(5) (OEM), with C(2) 215 being the API default. 216 - also see the last note in the I(ipmi_authtype) documentation 217 version_added: '2.5' 218 type: int 219 ipmi_username: 220 description: 221 - IPMI username. 222 - also see the last note in the I(ipmi_authtype) documentation 223 version_added: '2.5' 224 type: str 225 ipmi_password: 226 description: 227 - IPMI password. 228 - also see the last note in the I(ipmi_authtype) documentation 229 version_added: '2.5' 230 type: str 231 force: 232 description: 233 - Overwrite the host configuration, even if already present. 234 type: bool 235 default: 'yes' 236 version_added: '2.0' 237extends_documentation_fragment: 238 - zabbix 239''' 240 241EXAMPLES = r''' 242- name: Create a new host or update an existing host's info 243 local_action: 244 module: zabbix_host 245 server_url: http://monitor.example.com 246 login_user: username 247 login_password: password 248 host_name: ExampleHost 249 visible_name: ExampleName 250 description: My ExampleHost Description 251 host_groups: 252 - Example group1 253 - Example group2 254 link_templates: 255 - Example template1 256 - Example template2 257 status: enabled 258 state: present 259 inventory_mode: manual 260 inventory_zabbix: 261 tag: "{{ your_tag }}" 262 alias: "{{ your_alias }}" 263 notes: "Special Informations: {{ your_informations | default('None') }}" 264 location: "{{ your_location }}" 265 site_rack: "{{ your_site_rack }}" 266 os: "{{ your_os }}" 267 hardware: "{{ your_hardware }}" 268 ipmi_authtype: 2 269 ipmi_privilege: 4 270 ipmi_username: username 271 ipmi_password: password 272 interfaces: 273 - type: 1 274 main: 1 275 useip: 1 276 ip: 10.xx.xx.xx 277 dns: "" 278 port: 10050 279 - type: 4 280 main: 1 281 useip: 1 282 ip: 10.xx.xx.xx 283 dns: "" 284 port: 12345 285 proxy: a.zabbix.proxy 286- name: Update an existing host's TLS settings 287 local_action: 288 module: zabbix_host 289 server_url: http://monitor.example.com 290 login_user: username 291 login_password: password 292 host_name: ExampleHost 293 visible_name: ExampleName 294 host_groups: 295 - Example group1 296 tls_psk_identity: test 297 tls_connect: 2 298 tls_psk: 123456789abcdef123456789abcdef12 299''' 300 301 302import atexit 303import copy 304import traceback 305 306try: 307 from zabbix_api import ZabbixAPI 308 HAS_ZABBIX_API = True 309except ImportError: 310 ZBX_IMP_ERR = traceback.format_exc() 311 HAS_ZABBIX_API = False 312 313from distutils.version import LooseVersion 314from ansible.module_utils.basic import AnsibleModule, missing_required_lib 315 316 317class Host(object): 318 def __init__(self, module, zbx): 319 self._module = module 320 self._zapi = zbx 321 self._zbx_api_version = zbx.api_version()[:5] 322 323 # exist host 324 def is_host_exist(self, host_name): 325 result = self._zapi.host.get({'filter': {'host': host_name}}) 326 return result 327 328 # check if host group exists 329 def check_host_group_exist(self, group_names): 330 for group_name in group_names: 331 result = self._zapi.hostgroup.get({'filter': {'name': group_name}}) 332 if not result: 333 self._module.fail_json(msg="Hostgroup not found: %s" % group_name) 334 return True 335 336 def get_template_ids(self, template_list): 337 template_ids = [] 338 if template_list is None or len(template_list) == 0: 339 return template_ids 340 for template in template_list: 341 template_list = self._zapi.template.get({'output': 'extend', 'filter': {'host': template}}) 342 if len(template_list) < 1: 343 self._module.fail_json(msg="Template not found: %s" % template) 344 else: 345 template_id = template_list[0]['templateid'] 346 template_ids.append(template_id) 347 return template_ids 348 349 def add_host(self, host_name, group_ids, status, interfaces, proxy_id, visible_name, description, tls_connect, 350 tls_accept, tls_psk_identity, tls_psk, tls_issuer, tls_subject, ipmi_authtype, ipmi_privilege, 351 ipmi_username, ipmi_password): 352 try: 353 if self._module.check_mode: 354 self._module.exit_json(changed=True) 355 parameters = {'host': host_name, 'interfaces': interfaces, 'groups': group_ids, 'status': status, 356 'tls_connect': tls_connect, 'tls_accept': tls_accept} 357 if proxy_id: 358 parameters['proxy_hostid'] = proxy_id 359 if visible_name: 360 parameters['name'] = visible_name 361 if tls_psk_identity is not None: 362 parameters['tls_psk_identity'] = tls_psk_identity 363 if tls_psk is not None: 364 parameters['tls_psk'] = tls_psk 365 if tls_issuer is not None: 366 parameters['tls_issuer'] = tls_issuer 367 if tls_subject is not None: 368 parameters['tls_subject'] = tls_subject 369 if description: 370 parameters['description'] = description 371 if ipmi_authtype is not None: 372 parameters['ipmi_authtype'] = ipmi_authtype 373 if ipmi_privilege is not None: 374 parameters['ipmi_privilege'] = ipmi_privilege 375 if ipmi_username is not None: 376 parameters['ipmi_username'] = ipmi_username 377 if ipmi_password is not None: 378 parameters['ipmi_password'] = ipmi_password 379 380 host_list = self._zapi.host.create(parameters) 381 if len(host_list) >= 1: 382 return host_list['hostids'][0] 383 except Exception as e: 384 self._module.fail_json(msg="Failed to create host %s: %s" % (host_name, e)) 385 386 def update_host(self, host_name, group_ids, status, host_id, interfaces, exist_interface_list, proxy_id, 387 visible_name, description, tls_connect, tls_accept, tls_psk_identity, tls_psk, tls_issuer, tls_subject, ipmi_authtype, 388 ipmi_privilege, ipmi_username, ipmi_password): 389 try: 390 if self._module.check_mode: 391 self._module.exit_json(changed=True) 392 parameters = {'hostid': host_id, 'groups': group_ids, 'status': status, 'tls_connect': tls_connect, 393 'tls_accept': tls_accept} 394 if proxy_id >= 0: 395 parameters['proxy_hostid'] = proxy_id 396 if visible_name: 397 parameters['name'] = visible_name 398 if tls_psk_identity: 399 parameters['tls_psk_identity'] = tls_psk_identity 400 if tls_psk: 401 parameters['tls_psk'] = tls_psk 402 if tls_issuer: 403 parameters['tls_issuer'] = tls_issuer 404 if tls_subject: 405 parameters['tls_subject'] = tls_subject 406 if description: 407 parameters['description'] = description 408 if ipmi_authtype: 409 parameters['ipmi_authtype'] = ipmi_authtype 410 if ipmi_privilege: 411 parameters['ipmi_privilege'] = ipmi_privilege 412 if ipmi_username: 413 parameters['ipmi_username'] = ipmi_username 414 if ipmi_password: 415 parameters['ipmi_password'] = ipmi_password 416 417 self._zapi.host.update(parameters) 418 interface_list_copy = exist_interface_list 419 if interfaces: 420 for interface in interfaces: 421 flag = False 422 interface_str = interface 423 for exist_interface in exist_interface_list: 424 interface_type = int(interface['type']) 425 exist_interface_type = int(exist_interface['type']) 426 if interface_type == exist_interface_type: 427 # update 428 interface_str['interfaceid'] = exist_interface['interfaceid'] 429 self._zapi.hostinterface.update(interface_str) 430 flag = True 431 interface_list_copy.remove(exist_interface) 432 break 433 if not flag: 434 # add 435 interface_str['hostid'] = host_id 436 self._zapi.hostinterface.create(interface_str) 437 # remove 438 remove_interface_ids = [] 439 for remove_interface in interface_list_copy: 440 interface_id = remove_interface['interfaceid'] 441 remove_interface_ids.append(interface_id) 442 if len(remove_interface_ids) > 0: 443 self._zapi.hostinterface.delete(remove_interface_ids) 444 except Exception as e: 445 self._module.fail_json(msg="Failed to update host %s: %s" % (host_name, e)) 446 447 def delete_host(self, host_id, host_name): 448 try: 449 if self._module.check_mode: 450 self._module.exit_json(changed=True) 451 self._zapi.host.delete([host_id]) 452 except Exception as e: 453 self._module.fail_json(msg="Failed to delete host %s: %s" % (host_name, e)) 454 455 # get host by host name 456 def get_host_by_host_name(self, host_name): 457 host_list = self._zapi.host.get({'output': 'extend', 'selectInventory': 'extend', 'filter': {'host': [host_name]}}) 458 if len(host_list) < 1: 459 self._module.fail_json(msg="Host not found: %s" % host_name) 460 else: 461 return host_list[0] 462 463 # get proxyid by proxy name 464 def get_proxyid_by_proxy_name(self, proxy_name): 465 proxy_list = self._zapi.proxy.get({'output': 'extend', 'filter': {'host': [proxy_name]}}) 466 if len(proxy_list) < 1: 467 self._module.fail_json(msg="Proxy not found: %s" % proxy_name) 468 else: 469 return int(proxy_list[0]['proxyid']) 470 471 # get group ids by group names 472 def get_group_ids_by_group_names(self, group_names): 473 group_ids = [] 474 if self.check_host_group_exist(group_names): 475 group_list = self._zapi.hostgroup.get({'output': 'extend', 'filter': {'name': group_names}}) 476 for group in group_list: 477 group_id = group['groupid'] 478 group_ids.append({'groupid': group_id}) 479 return group_ids 480 481 # get host templates by host id 482 def get_host_templates_by_host_id(self, host_id): 483 template_ids = [] 484 template_list = self._zapi.template.get({'output': 'extend', 'hostids': host_id}) 485 for template in template_list: 486 template_ids.append(template['templateid']) 487 return template_ids 488 489 # get host groups by host id 490 def get_host_groups_by_host_id(self, host_id): 491 exist_host_groups = [] 492 host_groups_list = self._zapi.hostgroup.get({'output': 'extend', 'hostids': host_id}) 493 494 if len(host_groups_list) >= 1: 495 for host_groups_name in host_groups_list: 496 exist_host_groups.append(host_groups_name['name']) 497 return exist_host_groups 498 499 # check the exist_interfaces whether it equals the interfaces or not 500 def check_interface_properties(self, exist_interface_list, interfaces): 501 interfaces_port_list = [] 502 503 if interfaces is not None: 504 if len(interfaces) >= 1: 505 for interface in interfaces: 506 interfaces_port_list.append(int(interface['port'])) 507 508 exist_interface_ports = [] 509 if len(exist_interface_list) >= 1: 510 for exist_interface in exist_interface_list: 511 exist_interface_ports.append(int(exist_interface['port'])) 512 513 if set(interfaces_port_list) != set(exist_interface_ports): 514 return True 515 516 for exist_interface in exist_interface_list: 517 exit_interface_port = int(exist_interface['port']) 518 for interface in interfaces: 519 interface_port = int(interface['port']) 520 if interface_port == exit_interface_port: 521 for key in interface.keys(): 522 if str(exist_interface[key]) != str(interface[key]): 523 return True 524 525 return False 526 527 # get the status of host by host 528 def get_host_status_by_host(self, host): 529 return host['status'] 530 531 # check all the properties before link or clear template 532 def check_all_properties(self, host_id, host_groups, status, interfaces, template_ids, 533 exist_interfaces, host, proxy_id, visible_name, description, host_name, 534 inventory_mode, inventory_zabbix, tls_accept, tls_psk_identity, tls_psk, 535 tls_issuer, tls_subject, tls_connect, ipmi_authtype, ipmi_privilege, 536 ipmi_username, ipmi_password): 537 # get the existing host's groups 538 exist_host_groups = self.get_host_groups_by_host_id(host_id) 539 if set(host_groups) != set(exist_host_groups): 540 return True 541 542 # get the existing status 543 exist_status = self.get_host_status_by_host(host) 544 if int(status) != int(exist_status): 545 return True 546 547 # check the exist_interfaces whether it equals the interfaces or not 548 if self.check_interface_properties(exist_interfaces, interfaces): 549 return True 550 551 # get the existing templates 552 exist_template_ids = self.get_host_templates_by_host_id(host_id) 553 if set(list(template_ids)) != set(exist_template_ids): 554 return True 555 556 if int(host['proxy_hostid']) != int(proxy_id): 557 return True 558 559 # Check whether the visible_name has changed; Zabbix defaults to the technical hostname if not set. 560 if visible_name: 561 if host['name'] != visible_name and host['name'] != host_name: 562 return True 563 564 # Only compare description if it is given as a module parameter 565 if description: 566 if host['description'] != description: 567 return True 568 569 if inventory_mode: 570 if LooseVersion(self._zbx_api_version) <= LooseVersion('4.4.0'): 571 if host['inventory']: 572 if int(host['inventory']['inventory_mode']) != self.inventory_mode_numeric(inventory_mode): 573 return True 574 elif inventory_mode != 'disabled': 575 return True 576 else: 577 if int(host['inventory_mode']) != self.inventory_mode_numeric(inventory_mode): 578 return True 579 580 if inventory_zabbix: 581 proposed_inventory = copy.deepcopy(host['inventory']) 582 proposed_inventory.update(inventory_zabbix) 583 if proposed_inventory != host['inventory']: 584 return True 585 586 if tls_accept is not None and 'tls_accept' in host: 587 if int(host['tls_accept']) != tls_accept: 588 return True 589 590 if tls_psk_identity is not None and 'tls_psk_identity' in host: 591 if host['tls_psk_identity'] != tls_psk_identity: 592 return True 593 594 if tls_psk is not None and 'tls_psk' in host: 595 if host['tls_psk'] != tls_psk: 596 return True 597 598 if tls_issuer is not None and 'tls_issuer' in host: 599 if host['tls_issuer'] != tls_issuer: 600 return True 601 602 if tls_subject is not None and 'tls_subject' in host: 603 if host['tls_subject'] != tls_subject: 604 return True 605 606 if tls_connect is not None and 'tls_connect' in host: 607 if int(host['tls_connect']) != tls_connect: 608 return True 609 if ipmi_authtype is not None: 610 if int(host['ipmi_authtype']) != ipmi_authtype: 611 return True 612 if ipmi_privilege is not None: 613 if int(host['ipmi_privilege']) != ipmi_privilege: 614 return True 615 if ipmi_username is not None: 616 if host['ipmi_username'] != ipmi_username: 617 return True 618 if ipmi_password is not None: 619 if host['ipmi_password'] != ipmi_password: 620 return True 621 622 return False 623 624 # link or clear template of the host 625 def link_or_clear_template(self, host_id, template_id_list, tls_connect, tls_accept, tls_psk_identity, tls_psk, 626 tls_issuer, tls_subject, ipmi_authtype, ipmi_privilege, ipmi_username, ipmi_password): 627 # get host's exist template ids 628 exist_template_id_list = self.get_host_templates_by_host_id(host_id) 629 630 exist_template_ids = set(exist_template_id_list) 631 template_ids = set(template_id_list) 632 template_id_list = list(template_ids) 633 634 # get unlink and clear templates 635 templates_clear = exist_template_ids.difference(template_ids) 636 templates_clear_list = list(templates_clear) 637 request_str = {'hostid': host_id, 'templates': template_id_list, 'templates_clear': templates_clear_list, 638 'tls_connect': tls_connect, 'tls_accept': tls_accept, 'ipmi_authtype': ipmi_authtype, 639 'ipmi_privilege': ipmi_privilege, 'ipmi_username': ipmi_username, 'ipmi_password': ipmi_password} 640 if tls_psk_identity is not None: 641 request_str['tls_psk_identity'] = tls_psk_identity 642 if tls_psk is not None: 643 request_str['tls_psk'] = tls_psk 644 if tls_issuer is not None: 645 request_str['tls_issuer'] = tls_issuer 646 if tls_subject is not None: 647 request_str['tls_subject'] = tls_subject 648 try: 649 if self._module.check_mode: 650 self._module.exit_json(changed=True) 651 self._zapi.host.update(request_str) 652 except Exception as e: 653 self._module.fail_json(msg="Failed to link template to host: %s" % e) 654 655 def inventory_mode_numeric(self, inventory_mode): 656 if inventory_mode == "automatic": 657 return int(1) 658 elif inventory_mode == "manual": 659 return int(0) 660 elif inventory_mode == "disabled": 661 return int(-1) 662 return inventory_mode 663 664 # Update the host inventory_mode 665 def update_inventory_mode(self, host_id, inventory_mode): 666 667 # nothing was set, do nothing 668 if not inventory_mode: 669 return 670 671 inventory_mode = self.inventory_mode_numeric(inventory_mode) 672 673 # watch for - https://support.zabbix.com/browse/ZBX-6033 674 request_str = {'hostid': host_id, 'inventory_mode': inventory_mode} 675 try: 676 if self._module.check_mode: 677 self._module.exit_json(changed=True) 678 self._zapi.host.update(request_str) 679 except Exception as e: 680 self._module.fail_json(msg="Failed to set inventory_mode to host: %s" % e) 681 682 def update_inventory_zabbix(self, host_id, inventory): 683 684 if not inventory: 685 return 686 687 request_str = {'hostid': host_id, 'inventory': inventory} 688 try: 689 if self._module.check_mode: 690 self._module.exit_json(changed=True) 691 self._zapi.host.update(request_str) 692 except Exception as e: 693 self._module.fail_json(msg="Failed to set inventory to host: %s" % e) 694 695 696def main(): 697 module = AnsibleModule( 698 argument_spec=dict( 699 server_url=dict(type='str', required=True, aliases=['url']), 700 login_user=dict(type='str', required=True), 701 login_password=dict(type='str', required=True, no_log=True), 702 host_name=dict(type='str', required=True), 703 http_login_user=dict(type='str', required=False, default=None), 704 http_login_password=dict(type='str', required=False, default=None, no_log=True), 705 validate_certs=dict(type='bool', required=False, default=True), 706 host_groups=dict(type='list', required=False), 707 link_templates=dict(type='list', required=False), 708 status=dict(type='str', default="enabled", choices=['enabled', 'disabled']), 709 state=dict(type='str', default="present", choices=['present', 'absent']), 710 inventory_mode=dict(type='str', required=False, choices=['automatic', 'manual', 'disabled']), 711 ipmi_authtype=dict(type='int', default=None), 712 ipmi_privilege=dict(type='int', default=None), 713 ipmi_username=dict(type='str', required=False, default=None), 714 ipmi_password=dict(type='str', required=False, default=None, no_log=True), 715 tls_connect=dict(type='int', default=1), 716 tls_accept=dict(type='int', default=1), 717 tls_psk_identity=dict(type='str', required=False), 718 tls_psk=dict(type='str', required=False), 719 ca_cert=dict(type='str', required=False, aliases=['tls_issuer']), 720 tls_subject=dict(type='str', required=False), 721 inventory_zabbix=dict(type='dict', required=False), 722 timeout=dict(type='int', default=10), 723 interfaces=dict(type='list', required=False), 724 force=dict(type='bool', default=True), 725 proxy=dict(type='str', required=False), 726 visible_name=dict(type='str', required=False), 727 description=dict(type='str', required=False) 728 ), 729 supports_check_mode=True 730 ) 731 732 if not HAS_ZABBIX_API: 733 module.fail_json(msg=missing_required_lib('zabbix-api', url='https://pypi.org/project/zabbix-api/'), exception=ZBX_IMP_ERR) 734 735 server_url = module.params['server_url'] 736 login_user = module.params['login_user'] 737 login_password = module.params['login_password'] 738 http_login_user = module.params['http_login_user'] 739 http_login_password = module.params['http_login_password'] 740 validate_certs = module.params['validate_certs'] 741 host_name = module.params['host_name'] 742 visible_name = module.params['visible_name'] 743 description = module.params['description'] 744 host_groups = module.params['host_groups'] 745 link_templates = module.params['link_templates'] 746 inventory_mode = module.params['inventory_mode'] 747 ipmi_authtype = module.params['ipmi_authtype'] 748 ipmi_privilege = module.params['ipmi_privilege'] 749 ipmi_username = module.params['ipmi_username'] 750 ipmi_password = module.params['ipmi_password'] 751 tls_connect = module.params['tls_connect'] 752 tls_accept = module.params['tls_accept'] 753 tls_psk_identity = module.params['tls_psk_identity'] 754 tls_psk = module.params['tls_psk'] 755 tls_issuer = module.params['ca_cert'] 756 tls_subject = module.params['tls_subject'] 757 inventory_zabbix = module.params['inventory_zabbix'] 758 status = module.params['status'] 759 state = module.params['state'] 760 timeout = module.params['timeout'] 761 interfaces = module.params['interfaces'] 762 force = module.params['force'] 763 proxy = module.params['proxy'] 764 765 # convert enabled to 0; disabled to 1 766 status = 1 if status == "disabled" else 0 767 768 zbx = None 769 # login to zabbix 770 try: 771 zbx = ZabbixAPI(server_url, timeout=timeout, user=http_login_user, passwd=http_login_password, 772 validate_certs=validate_certs) 773 zbx.login(login_user, login_password) 774 atexit.register(zbx.logout) 775 except Exception as e: 776 module.fail_json(msg="Failed to connect to Zabbix server: %s" % e) 777 778 host = Host(module, zbx) 779 780 template_ids = [] 781 if link_templates: 782 template_ids = host.get_template_ids(link_templates) 783 784 group_ids = [] 785 786 if host_groups: 787 group_ids = host.get_group_ids_by_group_names(host_groups) 788 789 ip = "" 790 if interfaces: 791 # ensure interfaces are well-formed 792 for interface in interfaces: 793 if 'type' not in interface: 794 module.fail_json(msg="(interface) type needs to be specified for interface '%s'." % interface) 795 interfacetypes = {'agent': 1, 'snmp': 2, 'ipmi': 3, 'jmx': 4} 796 if interface['type'] in interfacetypes.keys(): 797 interface['type'] = interfacetypes[interface['type']] 798 if interface['type'] < 1 or interface['type'] > 4: 799 module.fail_json(msg="Interface type can only be 1-4 for interface '%s'." % interface) 800 if 'useip' not in interface: 801 interface['useip'] = 0 802 if 'dns' not in interface: 803 if interface['useip'] == 0: 804 module.fail_json(msg="dns needs to be set if useip is 0 on interface '%s'." % interface) 805 interface['dns'] = '' 806 if 'ip' not in interface: 807 if interface['useip'] == 1: 808 module.fail_json(msg="ip needs to be set if useip is 1 on interface '%s'." % interface) 809 interface['ip'] = '' 810 if 'main' not in interface: 811 interface['main'] = 0 812 if 'port' not in interface: 813 if interface['type'] == 1: 814 interface['port'] = "10050" 815 elif interface['type'] == 2: 816 interface['port'] = "161" 817 elif interface['type'] == 3: 818 interface['port'] = "623" 819 elif interface['type'] == 4: 820 interface['port'] = "12345" 821 822 if interface['type'] == 1: 823 ip = interface['ip'] 824 825 # Use proxy specified, or set to 0 826 if proxy: 827 proxy_id = host.get_proxyid_by_proxy_name(proxy) 828 else: 829 proxy_id = 0 830 831 # check if host exist 832 is_host_exist = host.is_host_exist(host_name) 833 834 if is_host_exist: 835 # get host id by host name 836 zabbix_host_obj = host.get_host_by_host_name(host_name) 837 host_id = zabbix_host_obj['hostid'] 838 839 # If proxy is not specified as a module parameter, use the existing setting 840 if proxy is None: 841 proxy_id = int(zabbix_host_obj['proxy_hostid']) 842 843 if state == "absent": 844 # remove host 845 host.delete_host(host_id, host_name) 846 module.exit_json(changed=True, result="Successfully delete host %s" % host_name) 847 else: 848 if not host_groups: 849 # if host_groups have not been specified when updating an existing host, just 850 # get the group_ids from the existing host without updating them. 851 host_groups = host.get_host_groups_by_host_id(host_id) 852 group_ids = host.get_group_ids_by_group_names(host_groups) 853 854 # get existing host's interfaces 855 exist_interfaces = host._zapi.hostinterface.get({'output': 'extend', 'hostids': host_id}) 856 857 # if no interfaces were specified with the module, start with an empty list 858 if not interfaces: 859 interfaces = [] 860 861 # When force=no is specified, append existing interfaces to interfaces to update. When 862 # no interfaces have been specified, copy existing interfaces as specified from the API. 863 # Do the same with templates and host groups. 864 if not force or not interfaces: 865 for interface in copy.deepcopy(exist_interfaces): 866 # remove values not used during hostinterface.add/update calls 867 for key in tuple(interface.keys()): 868 if key in ['interfaceid', 'hostid', 'bulk']: 869 interface.pop(key, None) 870 871 for index in interface.keys(): 872 if index in ['useip', 'main', 'type', 'port']: 873 interface[index] = int(interface[index]) 874 875 if interface not in interfaces: 876 interfaces.append(interface) 877 878 if not force or link_templates is None: 879 template_ids = list(set(template_ids + host.get_host_templates_by_host_id(host_id))) 880 881 if not force: 882 for group_id in host.get_group_ids_by_group_names(host.get_host_groups_by_host_id(host_id)): 883 if group_id not in group_ids: 884 group_ids.append(group_id) 885 886 # update host 887 if host.check_all_properties(host_id, host_groups, status, interfaces, template_ids, 888 exist_interfaces, zabbix_host_obj, proxy_id, visible_name, 889 description, host_name, inventory_mode, inventory_zabbix, 890 tls_accept, tls_psk_identity, tls_psk, tls_issuer, tls_subject, tls_connect, 891 ipmi_authtype, ipmi_privilege, ipmi_username, ipmi_password): 892 host.update_host(host_name, group_ids, status, host_id, 893 interfaces, exist_interfaces, proxy_id, visible_name, description, tls_connect, tls_accept, 894 tls_psk_identity, tls_psk, tls_issuer, tls_subject, ipmi_authtype, ipmi_privilege, ipmi_username, ipmi_password) 895 host.link_or_clear_template(host_id, template_ids, tls_connect, tls_accept, tls_psk_identity, 896 tls_psk, tls_issuer, tls_subject, ipmi_authtype, ipmi_privilege, 897 ipmi_username, ipmi_password) 898 host.update_inventory_mode(host_id, inventory_mode) 899 host.update_inventory_zabbix(host_id, inventory_zabbix) 900 901 module.exit_json(changed=True, 902 result="Successfully update host %s (%s) and linked with template '%s'" 903 % (host_name, ip, link_templates)) 904 else: 905 module.exit_json(changed=False) 906 907 else: 908 if state == "absent": 909 # the host is already deleted. 910 module.exit_json(changed=False) 911 912 if not group_ids: 913 module.fail_json(msg="Specify at least one group for creating host '%s'." % host_name) 914 915 if not interfaces or (interfaces and len(interfaces) == 0): 916 module.fail_json(msg="Specify at least one interface for creating host '%s'." % host_name) 917 918 # create host 919 host_id = host.add_host(host_name, group_ids, status, interfaces, proxy_id, visible_name, description, tls_connect, 920 tls_accept, tls_psk_identity, tls_psk, tls_issuer, tls_subject, ipmi_authtype, ipmi_privilege, 921 ipmi_username, ipmi_password) 922 host.link_or_clear_template(host_id, template_ids, tls_connect, tls_accept, tls_psk_identity, 923 tls_psk, tls_issuer, tls_subject, ipmi_authtype, ipmi_privilege, ipmi_username, ipmi_password) 924 host.update_inventory_mode(host_id, inventory_mode) 925 host.update_inventory_zabbix(host_id, inventory_zabbix) 926 module.exit_json(changed=True, result="Successfully added host %s (%s) and linked with template '%s'" % ( 927 host_name, ip, link_templates)) 928 929 930if __name__ == '__main__': 931 main() 932