1#!/usr/bin/python 2# This file is part of Ansible 3# 4# Ansible is free software: you can redistribute it and/or modify 5# it under the terms of the GNU General Public License as published by 6# the Free Software Foundation, either version 3 of the License, or 7# (at your option) any later version. 8# 9# Ansible is distributed in the hope that it will be useful, 10# but WITHOUT ANY WARRANTY; without even the implied warranty of 11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12# GNU General Public License for more details. 13# 14# You should have received a copy of the GNU General Public License 15# along with Ansible. If not, see <http://www.gnu.org/licenses/>. 16 17from __future__ import absolute_import, division, print_function 18__metaclass__ = type 19 20ANSIBLE_METADATA = {'metadata_version': '1.1', 21 'status': ['preview'], 22 'supported_by': 'community'} 23 24DOCUMENTATION = ''' 25--- 26module: oneandone_public_ip 27short_description: Configure 1&1 public IPs. 28description: 29 - Create, update, and remove public IPs. 30 This module has a dependency on 1and1 >= 1.0 31version_added: "2.5" 32options: 33 state: 34 description: 35 - Define a public ip state to create, remove, or update. 36 required: false 37 default: 'present' 38 choices: [ "present", "absent", "update" ] 39 auth_token: 40 description: 41 - Authenticating API token provided by 1&1. 42 required: true 43 api_url: 44 description: 45 - Custom API URL. Overrides the 46 ONEANDONE_API_URL environement variable. 47 required: false 48 reverse_dns: 49 description: 50 - Reverse DNS name. maxLength=256 51 required: false 52 datacenter: 53 description: 54 - ID of the datacenter where the IP will be created (only for unassigned IPs). 55 required: false 56 type: 57 description: 58 - Type of IP. Currently, only IPV4 is available. 59 choices: ["IPV4", "IPV6"] 60 default: 'IPV4' 61 required: false 62 public_ip_id: 63 description: 64 - The ID of the public IP used with update and delete states. 65 required: true 66 wait: 67 description: 68 - wait for the instance to be in state 'running' before returning 69 required: false 70 default: "yes" 71 type: bool 72 wait_timeout: 73 description: 74 - how long before wait gives up, in seconds 75 default: 600 76 wait_interval: 77 description: 78 - Defines the number of seconds to wait when using the _wait_for methods 79 default: 5 80 81requirements: 82 - "1and1" 83 - "python >= 2.6" 84 85author: 86 - Amel Ajdinovic (@aajdinov) 87 - Ethan Devenport (@edevenport) 88''' 89 90EXAMPLES = ''' 91 92# Create a public IP. 93 94- oneandone_public_ip: 95 auth_token: oneandone_private_api_key 96 reverse_dns: example.com 97 datacenter: US 98 type: IPV4 99 100# Update a public IP. 101 102- oneandone_public_ip: 103 auth_token: oneandone_private_api_key 104 public_ip_id: public ip id 105 reverse_dns: secondexample.com 106 state: update 107 108 109# Delete a public IP 110 111- oneandone_public_ip: 112 auth_token: oneandone_private_api_key 113 public_ip_id: public ip id 114 state: absent 115 116''' 117 118RETURN = ''' 119public_ip: 120 description: Information about the public ip that was processed 121 type: dict 122 sample: '{"id": "F77CC589EBC120905B4F4719217BFF6D", "ip": "10.5.132.106"}' 123 returned: always 124''' 125 126import os 127from ansible.module_utils.basic import AnsibleModule 128from ansible.module_utils.oneandone import ( 129 get_datacenter, 130 get_public_ip, 131 OneAndOneResources, 132 wait_for_resource_creation_completion 133) 134 135HAS_ONEANDONE_SDK = True 136 137try: 138 import oneandone.client 139except ImportError: 140 HAS_ONEANDONE_SDK = False 141 142DATACENTERS = ['US', 'ES', 'DE', 'GB'] 143 144TYPES = ['IPV4', 'IPV6'] 145 146 147def _check_mode(module, result): 148 if module.check_mode: 149 module.exit_json( 150 changed=result 151 ) 152 153 154def create_public_ip(module, oneandone_conn): 155 """ 156 Create new public IP 157 158 module : AnsibleModule object 159 oneandone_conn: authenticated oneandone object 160 161 Returns a dictionary containing a 'changed' attribute indicating whether 162 any public IP was added. 163 """ 164 reverse_dns = module.params.get('reverse_dns') 165 datacenter = module.params.get('datacenter') 166 ip_type = module.params.get('type') 167 wait = module.params.get('wait') 168 wait_timeout = module.params.get('wait_timeout') 169 wait_interval = module.params.get('wait_interval') 170 171 if datacenter is not None: 172 datacenter_id = get_datacenter(oneandone_conn, datacenter) 173 if datacenter_id is None: 174 _check_mode(module, False) 175 module.fail_json( 176 msg='datacenter %s not found.' % datacenter) 177 178 try: 179 _check_mode(module, True) 180 public_ip = oneandone_conn.create_public_ip( 181 reverse_dns=reverse_dns, 182 ip_type=ip_type, 183 datacenter_id=datacenter_id) 184 185 if wait: 186 wait_for_resource_creation_completion(oneandone_conn, 187 OneAndOneResources.public_ip, 188 public_ip['id'], 189 wait_timeout, 190 wait_interval) 191 public_ip = oneandone_conn.get_public_ip(public_ip['id']) 192 193 changed = True if public_ip else False 194 195 return (changed, public_ip) 196 except Exception as e: 197 module.fail_json(msg=str(e)) 198 199 200def update_public_ip(module, oneandone_conn): 201 """ 202 Update a public IP 203 204 module : AnsibleModule object 205 oneandone_conn: authenticated oneandone object 206 207 Returns a dictionary containing a 'changed' attribute indicating whether 208 any public IP was changed. 209 """ 210 reverse_dns = module.params.get('reverse_dns') 211 public_ip_id = module.params.get('public_ip_id') 212 wait = module.params.get('wait') 213 wait_timeout = module.params.get('wait_timeout') 214 wait_interval = module.params.get('wait_interval') 215 216 public_ip = get_public_ip(oneandone_conn, public_ip_id, True) 217 if public_ip is None: 218 _check_mode(module, False) 219 module.fail_json( 220 msg='public IP %s not found.' % public_ip_id) 221 222 try: 223 _check_mode(module, True) 224 public_ip = oneandone_conn.modify_public_ip( 225 ip_id=public_ip['id'], 226 reverse_dns=reverse_dns) 227 228 if wait: 229 wait_for_resource_creation_completion(oneandone_conn, 230 OneAndOneResources.public_ip, 231 public_ip['id'], 232 wait_timeout, 233 wait_interval) 234 public_ip = oneandone_conn.get_public_ip(public_ip['id']) 235 236 changed = True if public_ip else False 237 238 return (changed, public_ip) 239 except Exception as e: 240 module.fail_json(msg=str(e)) 241 242 243def delete_public_ip(module, oneandone_conn): 244 """ 245 Delete a public IP 246 247 module : AnsibleModule object 248 oneandone_conn: authenticated oneandone object 249 250 Returns a dictionary containing a 'changed' attribute indicating whether 251 any public IP was deleted. 252 """ 253 public_ip_id = module.params.get('public_ip_id') 254 255 public_ip = get_public_ip(oneandone_conn, public_ip_id, True) 256 if public_ip is None: 257 _check_mode(module, False) 258 module.fail_json( 259 msg='public IP %s not found.' % public_ip_id) 260 261 try: 262 _check_mode(module, True) 263 deleted_public_ip = oneandone_conn.delete_public_ip( 264 ip_id=public_ip['id']) 265 266 changed = True if deleted_public_ip else False 267 268 return (changed, { 269 'id': public_ip['id'] 270 }) 271 except Exception as e: 272 module.fail_json(msg=str(e)) 273 274 275def main(): 276 module = AnsibleModule( 277 argument_spec=dict( 278 auth_token=dict( 279 type='str', 280 default=os.environ.get('ONEANDONE_AUTH_TOKEN'), 281 no_log=True), 282 api_url=dict( 283 type='str', 284 default=os.environ.get('ONEANDONE_API_URL')), 285 public_ip_id=dict(type='str'), 286 reverse_dns=dict(type='str'), 287 datacenter=dict( 288 choices=DATACENTERS, 289 default='US'), 290 type=dict( 291 choices=TYPES, 292 default='IPV4'), 293 wait=dict(type='bool', default=True), 294 wait_timeout=dict(type='int', default=600), 295 wait_interval=dict(type='int', default=5), 296 state=dict(type='str', default='present', choices=['present', 'absent', 'update']), 297 ), 298 supports_check_mode=True 299 ) 300 301 if not HAS_ONEANDONE_SDK: 302 module.fail_json(msg='1and1 required for this module') 303 304 if not module.params.get('auth_token'): 305 module.fail_json( 306 msg='auth_token parameter is required.') 307 308 if not module.params.get('api_url'): 309 oneandone_conn = oneandone.client.OneAndOneService( 310 api_token=module.params.get('auth_token')) 311 else: 312 oneandone_conn = oneandone.client.OneAndOneService( 313 api_token=module.params.get('auth_token'), api_url=module.params.get('api_url')) 314 315 state = module.params.get('state') 316 317 if state == 'absent': 318 if not module.params.get('public_ip_id'): 319 module.fail_json( 320 msg="'public_ip_id' parameter is required to delete a public ip.") 321 try: 322 (changed, public_ip) = delete_public_ip(module, oneandone_conn) 323 except Exception as e: 324 module.fail_json(msg=str(e)) 325 elif state == 'update': 326 if not module.params.get('public_ip_id'): 327 module.fail_json( 328 msg="'public_ip_id' parameter is required to update a public ip.") 329 try: 330 (changed, public_ip) = update_public_ip(module, oneandone_conn) 331 except Exception as e: 332 module.fail_json(msg=str(e)) 333 334 elif state == 'present': 335 try: 336 (changed, public_ip) = create_public_ip(module, oneandone_conn) 337 except Exception as e: 338 module.fail_json(msg=str(e)) 339 340 module.exit_json(changed=changed, public_ip=public_ip) 341 342 343if __name__ == '__main__': 344 main() 345