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: profitbricks_volume_attachments 13short_description: Attach or detach a volume. 14description: 15 - Allows you to attach or detach a volume from a ProfitBricks server. This module has a dependency on profitbricks >= 1.0.0 16options: 17 datacenter: 18 description: 19 - The datacenter in which to operate. 20 type: str 21 server: 22 description: 23 - The name of the server you wish to detach or attach the volume. 24 type: str 25 volume: 26 description: 27 - The volume name or ID. 28 type: str 29 subscription_user: 30 description: 31 - The ProfitBricks username. Overrides the PB_SUBSCRIPTION_ID environment variable. 32 type: str 33 required: false 34 subscription_password: 35 description: 36 - THe ProfitBricks password. Overrides the PB_PASSWORD environment variable. 37 type: str 38 required: false 39 wait: 40 description: 41 - wait for the operation to complete before returning 42 required: false 43 default: "yes" 44 type: bool 45 wait_timeout: 46 description: 47 - how long before wait gives up, in seconds 48 type: int 49 default: 600 50 state: 51 description: 52 - Indicate desired state of the resource 53 - "The available choices are: C(present), C(absent)." 54 type: str 55 required: false 56 default: 'present' 57 58requirements: [ "profitbricks" ] 59author: Matt Baldwin (@baldwinSPC) <baldwin@stackpointcloud.com> 60''' 61 62EXAMPLES = ''' 63- name: Attach a volume 64 community.general.profitbricks_volume_attachments: 65 datacenter: Tardis One 66 server: node002 67 volume: vol01 68 wait_timeout: 500 69 state: present 70 71- name: Detach a volume 72 community.general.profitbricks_volume_attachments: 73 datacenter: Tardis One 74 server: node002 75 volume: vol01 76 wait_timeout: 500 77 state: absent 78''' 79 80import re 81import time 82 83HAS_PB_SDK = True 84try: 85 from profitbricks.client import ProfitBricksService 86except ImportError: 87 HAS_PB_SDK = False 88 89from ansible.module_utils.basic import AnsibleModule 90 91 92uuid_match = re.compile( 93 r'[\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12}', re.I) 94 95 96def _wait_for_completion(profitbricks, promise, wait_timeout, msg): 97 if not promise: 98 return 99 wait_timeout = time.time() + wait_timeout 100 while wait_timeout > time.time(): 101 time.sleep(5) 102 operation_result = profitbricks.get_request( 103 request_id=promise['requestId'], 104 status=True) 105 106 if operation_result['metadata']['status'] == "DONE": 107 return 108 elif operation_result['metadata']['status'] == "FAILED": 109 raise Exception( 110 'Request failed to complete ' + msg + ' "' + str( 111 promise['requestId']) + '" to complete.') 112 113 raise Exception( 114 'Timed out waiting for async operation ' + msg + ' "' + str( 115 promise['requestId'] 116 ) + '" to complete.') 117 118 119def attach_volume(module, profitbricks): 120 """ 121 Attaches a volume. 122 123 This will attach a volume to the server. 124 125 module : AnsibleModule object 126 profitbricks: authenticated profitbricks object. 127 128 Returns: 129 True if the volume was attached, false otherwise 130 """ 131 datacenter = module.params.get('datacenter') 132 server = module.params.get('server') 133 volume = module.params.get('volume') 134 135 # Locate UUID for Datacenter 136 if not (uuid_match.match(datacenter)): 137 datacenter_list = profitbricks.list_datacenters() 138 for d in datacenter_list['items']: 139 dc = profitbricks.get_datacenter(d['id']) 140 if datacenter == dc['properties']['name']: 141 datacenter = d['id'] 142 break 143 144 # Locate UUID for Server 145 if not (uuid_match.match(server)): 146 server_list = profitbricks.list_servers(datacenter) 147 for s in server_list['items']: 148 if server == s['properties']['name']: 149 server = s['id'] 150 break 151 152 # Locate UUID for Volume 153 if not (uuid_match.match(volume)): 154 volume_list = profitbricks.list_volumes(datacenter) 155 for v in volume_list['items']: 156 if volume == v['properties']['name']: 157 volume = v['id'] 158 break 159 160 return profitbricks.attach_volume(datacenter, server, volume) 161 162 163def detach_volume(module, profitbricks): 164 """ 165 Detaches a volume. 166 167 This will remove a volume from the server. 168 169 module : AnsibleModule object 170 profitbricks: authenticated profitbricks object. 171 172 Returns: 173 True if the volume was detached, false otherwise 174 """ 175 datacenter = module.params.get('datacenter') 176 server = module.params.get('server') 177 volume = module.params.get('volume') 178 179 # Locate UUID for Datacenter 180 if not (uuid_match.match(datacenter)): 181 datacenter_list = profitbricks.list_datacenters() 182 for d in datacenter_list['items']: 183 dc = profitbricks.get_datacenter(d['id']) 184 if datacenter == dc['properties']['name']: 185 datacenter = d['id'] 186 break 187 188 # Locate UUID for Server 189 if not (uuid_match.match(server)): 190 server_list = profitbricks.list_servers(datacenter) 191 for s in server_list['items']: 192 if server == s['properties']['name']: 193 server = s['id'] 194 break 195 196 # Locate UUID for Volume 197 if not (uuid_match.match(volume)): 198 volume_list = profitbricks.list_volumes(datacenter) 199 for v in volume_list['items']: 200 if volume == v['properties']['name']: 201 volume = v['id'] 202 break 203 204 return profitbricks.detach_volume(datacenter, server, volume) 205 206 207def main(): 208 module = AnsibleModule( 209 argument_spec=dict( 210 datacenter=dict(), 211 server=dict(), 212 volume=dict(), 213 subscription_user=dict(), 214 subscription_password=dict(no_log=True), 215 wait=dict(type='bool', default=True), 216 wait_timeout=dict(type='int', default=600), 217 state=dict(default='present'), 218 ) 219 ) 220 221 if not HAS_PB_SDK: 222 module.fail_json(msg='profitbricks required for this module') 223 224 if not module.params.get('subscription_user'): 225 module.fail_json(msg='subscription_user parameter is required') 226 if not module.params.get('subscription_password'): 227 module.fail_json(msg='subscription_password parameter is required') 228 if not module.params.get('datacenter'): 229 module.fail_json(msg='datacenter parameter is required') 230 if not module.params.get('server'): 231 module.fail_json(msg='server parameter is required') 232 if not module.params.get('volume'): 233 module.fail_json(msg='volume parameter is required') 234 235 subscription_user = module.params.get('subscription_user') 236 subscription_password = module.params.get('subscription_password') 237 238 profitbricks = ProfitBricksService( 239 username=subscription_user, 240 password=subscription_password) 241 242 state = module.params.get('state') 243 244 if state == 'absent': 245 try: 246 (changed) = detach_volume(module, profitbricks) 247 module.exit_json(changed=changed) 248 except Exception as e: 249 module.fail_json(msg='failed to set volume_attach state: %s' % str(e)) 250 elif state == 'present': 251 try: 252 attach_volume(module, profitbricks) 253 module.exit_json() 254 except Exception as e: 255 module.fail_json(msg='failed to set volume_attach state: %s' % str(e)) 256 257 258if __name__ == '__main__': 259 main() 260