'Attach a volume to an instance.'

@extensions.expected_errors((400, 404, 409))
@validation.schema(volumes_schema.create_volume_attachment, '2.0', '2.48')
@validation.schema(volumes_schema.create_volume_attachment_v249, '2.49')
def create(self, req, server_id, body):

    context = req.environ['nova.context']
    context.can((va_policies.POLICY_ROOT % 'create'))
    volume_id = body['volumeAttachment']['volumeId']
    device = body['volumeAttachment'].get('device')
    tag = body['volumeAttachment'].get('tag')
    instance = common.get_instance(self.compute_api, context, server_id)
    if (instance.vm_state in (vm_states.SHELVED, vm_states.SHELVED_OFFLOADED)):
        _check_request_version(req, '2.20', 'attach_volume', server_id, instance.vm_state)
    try:
        device = self.compute_api.attach_volume(context, instance, volume_id, device, tag=tag)
    except (exception.InstanceUnknownCell, exception.VolumeNotFound) as e:
        raise exc.HTTPNotFound(explanation=e.format_message())
    except (exception.InstanceIsLocked, exception.DevicePathInUse) as e:
        raise exc.HTTPConflict(explanation=e.format_message())
    except exception.InstanceInvalidState as state_error:
        common.raise_http_conflict_for_instance_invalid_state(state_error, 'attach_volume', server_id)
    except (exception.InvalidVolume, exception.InvalidDevicePath, exception.InvalidInput, exception.TaggedAttachmentNotSupported) as e:
        raise exc.HTTPBadRequest(explanation=e.format_message())
    attachment = {}
    attachment['id'] = volume_id
    attachment['serverId'] = server_id
    attachment['volumeId'] = volume_id
    attachment['device'] = device
    return {'volumeAttachment': attachment}
