rax_cbs_attachments 8.22 KB
Newer Older
1
#!/usr/bin/python
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.

17 18
# This is a DOCUMENTATION stub specific to this module, it extends
# a documentation fragment located in ansible.utils.module_docs_fragments
19 20 21 22 23 24
DOCUMENTATION = '''
---
module: rax_cbs_attachments
short_description: Manipulate Rackspace Cloud Block Storage Volume Attachments
description:
     - Manipulate Rackspace Cloud Block Storage Volume Attachments
25
version_added: 1.6
26
options:
27 28 29
  device:
    description:
      - The device path to attach the volume to, e.g. /dev/xvde
30 31
    default: null
    required: true
32
  volume:
33 34 35 36 37 38 39 40 41 42 43 44
    description:
      - Name or id of the volume to attach/detach
    default: null
    required: true
  server:
    description:
      - Name or id of the server to attach/detach
    default: null
    required: true
  state:
    description:
      - Indicate desired state of the resource
45 46 47
    choices:
      - present
      - absent
48 49 50 51 52 53
    default: present
    required: true
  wait:
    description:
      - wait for the volume to be in 'in-use'/'available' state before returning
    default: "no"
54 55 56
    choices:
      - "yes"
      - "no"
57 58 59 60 61
  wait_timeout:
    description:
      - how long before wait gives up, in seconds
    default: 300
author: Christopher H. Laco, Matt Martz
62
extends_documentation_fragment: rackspace.openstack
63 64 65 66 67 68 69 70 71 72 73 74
'''

EXAMPLES = '''
- name: Attach a Block Storage Volume
  gather_facts: False
  hosts: local
  connection: local
  tasks:
    - name: Storage volume attach request
      local_action:
        module: rax_cbs_attachments
        credentials: ~/.raxpub
75
        volume: my-volume
76
        server: my-server
77
        device: /dev/xvdd
78 79 80 81 82 83 84 85
        region: DFW
        wait: yes
        state: present
      register: my_volume
'''

import sys

86
from uuid import UUID
87 88 89 90
from types import NoneType

try:
    import pyrax
91
    HAS_PYRAX = True
92
except ImportError:
93
    HAS_PYRAX = False
94 95 96 97

NON_CALLABLES = (basestring, bool, dict, int, list, NoneType)


98
def cloud_block_storage_attachments(module, state, volume, server, device,
99
                                    wait, wait_timeout):
100
    for arg in (state, volume, server, device):
101
        if not arg:
102 103
            module.fail_json(msg='%s is required for rax_cbs_attachments' %
                                 arg)
104 105 106

    cbs = pyrax.cloud_blockstorage
    cs = pyrax.cloudservers
107 108 109 110 111 112

    if cbs is None or cs is None:
        module.fail_json(msg='Failed to instantiate client. This '
                             'typically indicates an invalid region or an '
                             'incorrectly capitalized region name.')

113 114 115
    changed = False
    instance = {}

116 117 118 119 120 121 122 123
    try:
        UUID(volume)
        volume = cbs.get(volume)
    except ValueError:
        try:
            volume = cbs.find(name=volume)
        except Exception, e:
            module.fail_json(msg='%s' % e)
124

125 126
    if not volume:
        module.fail_json(msg='No matching storage volumes were found')
127 128

    if state == 'present':
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
        try:
            UUID(server)
            server = cs.servers.get(server)
        except ValueError:
            servers = cs.servers.list(search_opts=dict(name='^%s$' % server))
            if not servers:
                module.fail_json(msg='No Server was matched by name, '
                                     'try using the Server ID instead')
            if len(servers) > 1:
                module.fail_json(msg='Multiple servers matched by name, '
                                     'try using the Server ID instead')

            # We made it this far, grab the first and hopefully only server
            # in the list
            server = servers[0]

        if (volume.attachments and
                volume.attachments[0]['server_id'] == server.id):
            changed = False
        elif volume.attachments:
            module.fail_json(msg='Volume is attached to another server')
150
        else:
151 152 153 154 155 156 157
            try:
                volume.attach_to_instance(server, mountpoint=device)
                changed = True
            except Exception, e:
                module.fail_json(msg='%s' % e.message)

            volume.get()
158 159 160 161 162 163 164 165 166 167 168

        for key, value in vars(volume).iteritems():
            if (isinstance(value, NON_CALLABLES) and
                    not key.startswith('_')):
                instance[key] = value

        result = dict(changed=changed, volume=instance)

        if volume.status == 'error':
            result['msg'] = '%s failed to build' % volume.id
        elif wait:
169
            attempts = wait_timeout / 5
170
            pyrax.utils.wait_until(volume, 'status', 'in-use',
171
                                   interval=5, attempts=attempts)
172 173 174 175 176 177 178

        if 'msg' in result:
            module.fail_json(**result)
        else:
            module.exit_json(**result)

    elif state == 'absent':
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
        try:
            UUID(server)
            server = cs.servers.get(server)
        except ValueError:
            servers = cs.servers.list(search_opts=dict(name='^%s$' % server))
            if not servers:
                module.fail_json(msg='No Server was matched by name, '
                                     'try using the Server ID instead')
            if len(servers) > 1:
                module.fail_json(msg='Multiple servers matched by name, '
                                     'try using the Server ID instead')

            # We made it this far, grab the first and hopefully only server
            # in the list
            server = servers[0]

        if (volume.attachments and
                volume.attachments[0]['server_id'] == server.id):
            try:
                volume.detach()
                if wait:
                    pyrax.utils.wait_until(volume, 'status', 'available',
                                           interval=3, attempts=0,
                                           verbose=False)
203
                changed = True
204 205 206 207 208 209 210
            except Exception, e:
                module.fail_json(msg='%s' % e.message)

            volume.get()
            changed = True
        elif volume.attachments:
            module.fail_json(msg='Volume is attached to another server')
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233

        for key, value in vars(volume).iteritems():
            if (isinstance(value, NON_CALLABLES) and
                    not key.startswith('_')):
                instance[key] = value

        result = dict(changed=changed, volume=instance)

        if volume.status == 'error':
            result['msg'] = '%s failed to build' % volume.id

        if 'msg' in result:
            module.fail_json(**result)
        else:
            module.exit_json(**result)

    module.exit_json(changed=changed, volume=instance)


def main():
    argument_spec = rax_argument_spec()
    argument_spec.update(
        dict(
234 235 236
            device=dict(required=True),
            volume=dict(required=True),
            server=dict(required=True),
237
            state=dict(default='present', choices=['present', 'absent']),
238
            wait=dict(type='bool', default=False),
239 240 241 242 243 244 245 246 247
            wait_timeout=dict(type='int', default=300)
        )
    )

    module = AnsibleModule(
        argument_spec=argument_spec,
        required_together=rax_required_together()
    )

248 249 250 251 252
    if not HAS_PYRAX:
        module.fail_json(msg='pyrax is required for this module')

    device = module.params.get('device')
    volume = module.params.get('volume')
253 254 255
    server = module.params.get('server')
    state = module.params.get('state')
    wait = module.params.get('wait')
256
    wait_timeout = module.params.get('wait_timeout')
257 258 259

    setup_rax_module(module, pyrax)

260
    cloud_block_storage_attachments(module, state, volume, server, device,
261 262 263 264 265 266 267 268
                                    wait, wait_timeout)

# import module snippets
from ansible.module_utils.basic import *
from ansible.module_utils.rax import *

### invoke the module
main()