1#!/usr/local/bin/python3.8 2# -*- coding: utf-8 -*- 3# Copyright: Ansible Project 4# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 5 6from __future__ import absolute_import, division, print_function 7__metaclass__ = type 8 9 10DOCUMENTATION = ''' 11--- 12module: rax_mon_check 13short_description: Create or delete a Rackspace Cloud Monitoring check for an 14 existing entity. 15description: 16- Create or delete a Rackspace Cloud Monitoring check associated with an 17 existing rax_mon_entity. A check is a specific test or measurement that is 18 performed, possibly from different monitoring zones, on the systems you 19 monitor. Rackspace monitoring module flow | rax_mon_entity -> 20 *rax_mon_check* -> rax_mon_notification -> rax_mon_notification_plan -> 21 rax_mon_alarm 22options: 23 state: 24 type: str 25 description: 26 - Ensure that a check with this C(label) exists or does not exist. 27 choices: ["present", "absent"] 28 default: present 29 entity_id: 30 type: str 31 description: 32 - ID of the rax_mon_entity to target with this check. 33 required: true 34 label: 35 type: str 36 description: 37 - Defines a label for this check, between 1 and 64 characters long. 38 required: true 39 check_type: 40 type: str 41 description: 42 - The type of check to create. C(remote.) checks may be created on any 43 rax_mon_entity. C(agent.) checks may only be created on rax_mon_entities 44 that have a non-null C(agent_id). 45 - | 46 Choices for this option are: 47 - C(remote.dns) 48 - C(remote.ftp-banner) 49 - C(remote.http) 50 - C(remote.imap-banner) 51 - C(remote.mssql-banner) 52 - C(remote.mysql-banner) 53 - C(remote.ping) 54 - C(remote.pop3-banner) 55 - C(remote.postgresql-banner) 56 - C(remote.smtp-banner) 57 - C(remote.smtp) 58 - C(remote.ssh) 59 - C(remote.tcp) 60 - C(remote.telnet-banner) 61 - C(agent.filesystem) 62 - C(agent.memory) 63 - C(agent.load_average) 64 - C(agent.cpu) 65 - C(agent.disk) 66 - C(agent.network) 67 - C(agent.plugin) 68 required: true 69 monitoring_zones_poll: 70 type: str 71 description: 72 - Comma-separated list of the names of the monitoring zones the check should 73 run from. Available monitoring zones include mzdfw, mzhkg, mziad, mzlon, 74 mzord and mzsyd. Required for remote.* checks; prohibited for agent.* checks. 75 target_hostname: 76 type: str 77 description: 78 - One of `target_hostname` and `target_alias` is required for remote.* checks, 79 but prohibited for agent.* checks. The hostname this check should target. 80 Must be a valid IPv4, IPv6, or FQDN. 81 target_alias: 82 type: str 83 description: 84 - One of `target_alias` and `target_hostname` is required for remote.* checks, 85 but prohibited for agent.* checks. Use the corresponding key in the entity's 86 `ip_addresses` hash to resolve an IP address to target. 87 details: 88 type: dict 89 description: 90 - Additional details specific to the check type. Must be a hash of strings 91 between 1 and 255 characters long, or an array or object containing 0 to 92 256 items. 93 disabled: 94 description: 95 - If "yes", ensure the check is created, but don't actually use it yet. 96 type: bool 97 default: false 98 metadata: 99 type: dict 100 description: 101 - Hash of arbitrary key-value pairs to accompany this check if it fires. 102 Keys and values must be strings between 1 and 255 characters long. 103 period: 104 type: int 105 description: 106 - The number of seconds between each time the check is performed. Must be 107 greater than the minimum period set on your account. 108 timeout: 109 type: int 110 description: 111 - The number of seconds this check will wait when attempting to collect 112 results. Must be less than the period. 113author: Ash Wilson (@smashwilson) 114extends_documentation_fragment: 115- community.general.rackspace.openstack 116 117''' 118 119EXAMPLES = ''' 120- name: Create a monitoring check 121 gather_facts: False 122 hosts: local 123 connection: local 124 tasks: 125 - name: Associate a check with an existing entity. 126 community.general.rax_mon_check: 127 credentials: ~/.rax_pub 128 state: present 129 entity_id: "{{ the_entity['entity']['id'] }}" 130 label: the_check 131 check_type: remote.ping 132 monitoring_zones_poll: mziad,mzord,mzdfw 133 details: 134 count: 10 135 meta: 136 hurf: durf 137 register: the_check 138''' 139 140try: 141 import pyrax 142 HAS_PYRAX = True 143except ImportError: 144 HAS_PYRAX = False 145 146from ansible.module_utils.basic import AnsibleModule 147from ansible_collections.community.general.plugins.module_utils.rax import rax_argument_spec, rax_required_together, setup_rax_module 148 149 150def cloud_check(module, state, entity_id, label, check_type, 151 monitoring_zones_poll, target_hostname, target_alias, details, 152 disabled, metadata, period, timeout): 153 154 # Coerce attributes. 155 156 if monitoring_zones_poll and not isinstance(monitoring_zones_poll, list): 157 monitoring_zones_poll = [monitoring_zones_poll] 158 159 if period: 160 period = int(period) 161 162 if timeout: 163 timeout = int(timeout) 164 165 changed = False 166 check = None 167 168 cm = pyrax.cloud_monitoring 169 if not cm: 170 module.fail_json(msg='Failed to instantiate client. This typically ' 171 'indicates an invalid region or an incorrectly ' 172 'capitalized region name.') 173 174 entity = cm.get_entity(entity_id) 175 if not entity: 176 module.fail_json(msg='Failed to instantiate entity. "%s" may not be' 177 ' a valid entity id.' % entity_id) 178 179 existing = [e for e in entity.list_checks() if e.label == label] 180 181 if existing: 182 check = existing[0] 183 184 if state == 'present': 185 if len(existing) > 1: 186 module.fail_json(msg='%s existing checks have a label of %s.' % 187 (len(existing), label)) 188 189 should_delete = False 190 should_create = False 191 should_update = False 192 193 if check: 194 # Details may include keys set to default values that are not 195 # included in the initial creation. 196 # 197 # Only force a recreation of the check if one of the *specified* 198 # keys is missing or has a different value. 199 if details: 200 for (key, value) in details.items(): 201 if key not in check.details: 202 should_delete = should_create = True 203 elif value != check.details[key]: 204 should_delete = should_create = True 205 206 should_update = label != check.label or \ 207 (target_hostname and target_hostname != check.target_hostname) or \ 208 (target_alias and target_alias != check.target_alias) or \ 209 (disabled != check.disabled) or \ 210 (metadata and metadata != check.metadata) or \ 211 (period and period != check.period) or \ 212 (timeout and timeout != check.timeout) or \ 213 (monitoring_zones_poll and monitoring_zones_poll != check.monitoring_zones_poll) 214 215 if should_update and not should_delete: 216 check.update(label=label, 217 disabled=disabled, 218 metadata=metadata, 219 monitoring_zones_poll=monitoring_zones_poll, 220 timeout=timeout, 221 period=period, 222 target_alias=target_alias, 223 target_hostname=target_hostname) 224 changed = True 225 else: 226 # The check doesn't exist yet. 227 should_create = True 228 229 if should_delete: 230 check.delete() 231 232 if should_create: 233 check = cm.create_check(entity, 234 label=label, 235 check_type=check_type, 236 target_hostname=target_hostname, 237 target_alias=target_alias, 238 monitoring_zones_poll=monitoring_zones_poll, 239 details=details, 240 disabled=disabled, 241 metadata=metadata, 242 period=period, 243 timeout=timeout) 244 changed = True 245 elif state == 'absent': 246 if check: 247 check.delete() 248 changed = True 249 else: 250 module.fail_json(msg='state must be either present or absent.') 251 252 if check: 253 check_dict = { 254 "id": check.id, 255 "label": check.label, 256 "type": check.type, 257 "target_hostname": check.target_hostname, 258 "target_alias": check.target_alias, 259 "monitoring_zones_poll": check.monitoring_zones_poll, 260 "details": check.details, 261 "disabled": check.disabled, 262 "metadata": check.metadata, 263 "period": check.period, 264 "timeout": check.timeout 265 } 266 module.exit_json(changed=changed, check=check_dict) 267 else: 268 module.exit_json(changed=changed) 269 270 271def main(): 272 argument_spec = rax_argument_spec() 273 argument_spec.update( 274 dict( 275 entity_id=dict(required=True), 276 label=dict(required=True), 277 check_type=dict(required=True), 278 monitoring_zones_poll=dict(), 279 target_hostname=dict(), 280 target_alias=dict(), 281 details=dict(type='dict', default={}), 282 disabled=dict(type='bool', default=False), 283 metadata=dict(type='dict', default={}), 284 period=dict(type='int'), 285 timeout=dict(type='int'), 286 state=dict(default='present', choices=['present', 'absent']) 287 ) 288 ) 289 290 module = AnsibleModule( 291 argument_spec=argument_spec, 292 required_together=rax_required_together() 293 ) 294 295 if not HAS_PYRAX: 296 module.fail_json(msg='pyrax is required for this module') 297 298 entity_id = module.params.get('entity_id') 299 label = module.params.get('label') 300 check_type = module.params.get('check_type') 301 monitoring_zones_poll = module.params.get('monitoring_zones_poll') 302 target_hostname = module.params.get('target_hostname') 303 target_alias = module.params.get('target_alias') 304 details = module.params.get('details') 305 disabled = module.boolean(module.params.get('disabled')) 306 metadata = module.params.get('metadata') 307 period = module.params.get('period') 308 timeout = module.params.get('timeout') 309 310 state = module.params.get('state') 311 312 setup_rax_module(module, pyrax) 313 314 cloud_check(module, state, entity_id, label, check_type, 315 monitoring_zones_poll, target_hostname, target_alias, details, 316 disabled, metadata, period, timeout) 317 318 319if __name__ == '__main__': 320 main() 321