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