# Buggy code
def main():
    argument_spec = ovirt_full_argument_spec(
        state=dict(
            choices=["present", "absent", "attached", "detached", "exported"],
            default="present",
        ),
        id=dict(default=None),
        name=dict(default=None, aliases=["alias"]),
        description=dict(default=None),
        vm_name=dict(default=None),
        vm_id=dict(default=None),
        size=dict(default=None),
        interface=dict(
            default=None,
        ),
        storage_domain=dict(default=None),
        storage_domains=dict(default=None, type="list"),
        profile=dict(default=None),
        quota_id=dict(default=None),
        format=dict(default="cow", choices=["raw", "cow"]),
        content_type=dict(default="data", choices=["data", "iso"]),
        sparse=dict(default=None, type="bool"),
        bootable=dict(default=None, type="bool"),
        shareable=dict(default=None, type="bool"),
        logical_unit=dict(default=None, type="dict"),
        download_image_path=dict(default=None),
        upload_image_path=dict(default=None, aliases=["image_path"]),
        force=dict(default=False, type="bool"),
        sparsify=dict(default=None, type="bool"),
        openstack_volume_type=dict(default=None),
        image_provider=dict(default=None),
        host=dict(default=None),
        wipe_after_delete=dict(type="bool", default=None),
        activate=dict(default=None, type="bool"),
    )
    module = AnsibleModule(
        argument_spec=argument_spec,
        supports_check_mode=True,
    )

    lun = module.params.get("logical_unit")
    host = module.params["host"]
    # Fail when host is specified with the LUN id. Lun id is needed to identify
    # an existing disk if already available inthe environment.
    if (host and lun is None) or (host and lun.get("id") is None):
        module.fail_json(
            msg="Can not use parameter host ({0!s}) without "
            "specifying the logical_unit id".format(host)
        )

    check_sdk(module)
    check_params(module)

    try:
        disk = None
        state = module.params["state"]
        auth = module.params.get("auth")
        connection = create_connection(auth)
        disks_service = connection.system_service().disks_service()
        disks_module = DisksModule(
            connection=connection,
            module=module,
            service=disks_service,
        )

        if lun:
            disk = _search_by_lun(disks_service, lun.get("id"))

        ret = None
        # First take care of creating the VM, if needed:
        if state in ("present", "detached", "attached"):
            ret = disks_module.create(
                entity=disk,
                search_params=searchable_attributes(module),
                result_state=otypes.DiskStatus.OK if lun is None else None,
                fail_condition=lambda d: d.status == otypes.DiskStatus.ILLEGAL
                if lun is None
                else False,
            )
            is_new_disk = ret["changed"]
            ret["changed"] = ret["changed"] or disks_module.update_storage_domains(
                ret["id"]
            )
            # We need to pass ID to the module, so in case we want detach/attach disk
            # we have this ID specified to attach/detach method:
            module.params["id"] = ret["id"] if disk is None else disk.id

            # Upload disk image in case it's new disk or force parameter is passed:
            if module.params["upload_image_path"] and (
                is_new_disk or module.params["force"]
            ):
                uploaded = upload_disk_image(connection, module)
                ret["changed"] = ret["changed"] or uploaded
            # Download disk image in case it's file don't exist or force parameter is passed:
            if module.params["download_image_path"] and (
                not os.path.isfile(module.params["download_image_path"])
                or module.params["force"]
            ):
                downloaded = download_disk_image(connection, module)
                ret["changed"] = ret["changed"] or downloaded

            # Disk sparsify, only if disk is of image type:
            disk = disks_service.disk_service(module.params["id"]).get()
            if disk.storage_type == otypes.DiskStorageType.IMAGE:
                ret = disks_module.action(
                    action="sparsify",
                    action_condition=lambda d: module.params["sparsify"],
                    wait_condition=lambda d: d.status == otypes.DiskStatus.OK,
                )

        # Export disk as image to glance domain
        elif state == "exported":
            disk = disks_module.search_entity()
            if disk is None:
                module.fail_json(
                    msg="Can not export given disk '%s', it doesn't exist"
                    % module.params.get("name")
                    or module.params.get("id")
                )
            if disk.storage_type == otypes.DiskStorageType.IMAGE:
                ret = disks_module.action(
                    action="export",
                    action_condition=lambda d: module.params["image_provider"],
                    wait_condition=lambda d: d.status == otypes.DiskStatus.OK,
                    storage_domain=otypes.StorageDomain(
                        name=module.params["image_provider"]
                    ),
                )
        elif state == "absent":
            ret = disks_module.remove()

        # If VM was passed attach/detach disks to/from the VM:
        if (
            module.params.get("vm_id") is not None
            or module.params.get("vm_name") is not None
            and state != "absent"
        ):
            vms_service = connection.system_service().vms_service()

            # If `vm_id` isn't specified, find VM by name:
            vm_id = module.params["vm_id"]
            if vm_id is None:
                vm_id = getattr(
                    search_by_name(vms_service, module.params["vm_name"]), "id", None
                )

            if vm_id is None:
                module.fail_json(msg="VM don't exists, please create it first.")

            disk_attachments_service = vms_service.vm_service(
                vm_id
            ).disk_attachments_service()
            disk_attachments_module = DiskAttachmentsModule(
                connection=connection,
                module=module,
                service=disk_attachments_service,
                changed=ret["changed"] if ret else False,
            )

            if state == "present" or state == "attached":
                ret = disk_attachments_module.create()
                if lun is None:
                    wait(
                        service=disk_attachments_service.service(ret["id"]),
                        condition=lambda d: follow_link(connection, d.disk).status
                        == otypes.DiskStatus.OK,
                        wait=module.params["wait"],
                        timeout=module.params["timeout"],
                    )
            elif state == "detached":
                ret = disk_attachments_module.remove()

        # When the host parameter is specified and the disk is not being
        # removed, refresh the information about the LUN.
        if state != "absent" and host:
            hosts_service = connection.system_service().hosts_service()
            host_id = get_id_by_name(hosts_service, host)
            disks_service.disk_service(disk.id).refresh_lun(otypes.Host(id=host_id))

        module.exit_json(**ret)
    except Exception as e:
        module.fail_json(msg=str(e), exception=traceback.format_exc())
    finally:
        connection.close(logout=auth.get("token") is None)


# Traceback (NotFoundError)
PLAY [xxxxxxx] ****************************************************

TASK [Gathering Facts] *********************************************************
ok: [xxxxxxx]

TASK [ovirt_auth] **************************************************************
ok: [xxxxxxx]

TASK [check_mode works with existing disk] *************************************
ok: [xxxxxxx]

TASK [check_mode does not work with new disk] **********************************
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: NotFoundError: HTTP response code is 404.
fatal: [xxxxxxx]: FAILED! => {"changed": false, "msg": "HTTP response code is 404."}
to retry, use: --limit @/home/td/ansible/test.retry

PLAY RECAP *********************************************************************
xxxxxxx       : ok=3    changed=0    unreachable=0    failed=1


The full traceback is:
Traceback (most recent call last):
File "/tmp/ansible_ovirt_disk_payload_FP4s8J/__main__.py", line 636, in main
disk = disks_service.disk_service(module.params['id']).get()
File "/usr/lib64/python2.7/site-packages/ovirtsdk4/services.py", line 35245, in get
return self._internal_get(headers, query, wait)
File "/usr/lib64/python2.7/site-packages/ovirtsdk4/service.py", line 211, in _internal_get
return future.wait() if wait else future
File "/usr/lib64/python2.7/site-packages/ovirtsdk4/service.py", line 55, in wait
return self._code(response)
File "/usr/lib64/python2.7/site-packages/ovirtsdk4/service.py", line 208, in callback
self._check_fault(response)
File "/usr/lib64/python2.7/site-packages/ovirtsdk4/service.py", line 130, in _check_fault
body = self._internal_read_body(response)
File "/usr/lib64/python2.7/site-packages/ovirtsdk4/service.py", line 312, in _internal_read_body
self._raise_error(response)
File "/usr/lib64/python2.7/site-packages/ovirtsdk4/service.py", line 118, in _raise_error
raise error
NotFoundError: HTTP response code is 404.

fatal: [xxxxxxx]: FAILED! => {
"changed": false,
"invocation": {
"module_args": {
"auth": {
"ca_file": null,
"compress": true,
"headers": null,
"insecure": true,
"kerberos": false,
"timeout": 0,
"token": "9OKJV9TH83wqW26XTL40jcGkOGivxam2csCJoT0PxGkBmn8_UVNHrW0263oaVHtYROhW5vOwr1PdHXIdOxRrMA",
"url": "https://xxxxxxx/ovirt-engine/api"
},
"bootable": null,
"description": null,
"download_image_path": null,
"fetch_nested": false,
"force": false,
"format": "cow",
"id": null,
"image_provider": null,
"interface": null,
"logical_unit": null,
"name": "new-disk1",
"nested_attributes": [],
"openstack_volume_type": null,
"poll_interval": 3,
"profile": null,
"quota_id": null,
"shareable": null,
"size": "1GiB",
"sparse": null,
"sparsify": null,
"state": "present",
"storage_domain": "VMstore",
"storage_domains": null,
"timeout": 180,
"upload_image_path": null,
"vm_id": null,
"vm_name": null,
"wait": true
}
},
"msg": "HTTP response code is 404."
}

# Fixed code
def main():
    argument_spec = ovirt_full_argument_spec(
        state=dict(
            choices=["present", "absent", "attached", "detached", "exported"],
            default="present",
        ),
        id=dict(default=None),
        name=dict(default=None, aliases=["alias"]),
        description=dict(default=None),
        vm_name=dict(default=None),
        vm_id=dict(default=None),
        size=dict(default=None),
        interface=dict(
            default=None,
        ),
        storage_domain=dict(default=None),
        storage_domains=dict(default=None, type="list"),
        profile=dict(default=None),
        quota_id=dict(default=None),
        format=dict(default="cow", choices=["raw", "cow"]),
        content_type=dict(default="data", choices=["data", "iso"]),
        sparse=dict(default=None, type="bool"),
        bootable=dict(default=None, type="bool"),
        shareable=dict(default=None, type="bool"),
        logical_unit=dict(default=None, type="dict"),
        download_image_path=dict(default=None),
        upload_image_path=dict(default=None, aliases=["image_path"]),
        force=dict(default=False, type="bool"),
        sparsify=dict(default=None, type="bool"),
        openstack_volume_type=dict(default=None),
        image_provider=dict(default=None),
        host=dict(default=None),
        wipe_after_delete=dict(type="bool", default=None),
        activate=dict(default=None, type="bool"),
    )
    module = AnsibleModule(
        argument_spec=argument_spec,
        supports_check_mode=True,
    )

    lun = module.params.get("logical_unit")
    host = module.params["host"]
    # Fail when host is specified with the LUN id. Lun id is needed to identify
    # an existing disk if already available inthe environment.
    if (host and lun is None) or (host and lun.get("id") is None):
        module.fail_json(
            msg="Can not use parameter host ({0!s}) without "
            "specifying the logical_unit id".format(host)
        )

    check_sdk(module)
    check_params(module)

    try:
        disk = None
        state = module.params["state"]
        auth = module.params.get("auth")
        connection = create_connection(auth)
        disks_service = connection.system_service().disks_service()
        disks_module = DisksModule(
            connection=connection,
            module=module,
            service=disks_service,
        )

        if lun:
            disk = _search_by_lun(disks_service, lun.get("id"))

        ret = None
        # First take care of creating the VM, if needed:
        if state in ("present", "detached", "attached"):
            ret = disks_module.create(
                entity=disk,
                search_params=searchable_attributes(module),
                result_state=otypes.DiskStatus.OK if lun is None else None,
                fail_condition=lambda d: d.status == otypes.DiskStatus.ILLEGAL
                if lun is None
                else False,
            )
            is_new_disk = ret["changed"]
            ret["changed"] = ret["changed"] or disks_module.update_storage_domains(
                ret["id"]
            )
            # We need to pass ID to the module, so in case we want detach/attach disk
            # we have this ID specified to attach/detach method:
            module.params["id"] = ret["id"] if disk is None else disk.id

            # Upload disk image in case it's new disk or force parameter is passed:
            if module.params["upload_image_path"] and (
                is_new_disk or module.params["force"]
            ):
                uploaded = upload_disk_image(connection, module)
                ret["changed"] = ret["changed"] or uploaded
            # Download disk image in case it's file don't exist or force parameter is passed:
            if module.params["download_image_path"] and (
                not os.path.isfile(module.params["download_image_path"])
                or module.params["force"]
            ):
                downloaded = download_disk_image(connection, module)
                ret["changed"] = ret["changed"] or downloaded

            # Disk sparsify, only if disk is of image type:
            if not module.check_mode:
                disk = disks_service.disk_service(module.params["id"]).get()
                if disk.storage_type == otypes.DiskStorageType.IMAGE:
                    ret = disks_module.action(
                        action="sparsify",
                        action_condition=lambda d: module.params["sparsify"],
                        wait_condition=lambda d: d.status == otypes.DiskStatus.OK,
                    )

        # Export disk as image to glance domain
        elif state == "exported":
            disk = disks_module.search_entity()
            if disk is None:
                module.fail_json(
                    msg="Can not export given disk '%s', it doesn't exist"
                    % module.params.get("name")
                    or module.params.get("id")
                )
            if disk.storage_type == otypes.DiskStorageType.IMAGE:
                ret = disks_module.action(
                    action="export",
                    action_condition=lambda d: module.params["image_provider"],
                    wait_condition=lambda d: d.status == otypes.DiskStatus.OK,
                    storage_domain=otypes.StorageDomain(
                        name=module.params["image_provider"]
                    ),
                )
        elif state == "absent":
            ret = disks_module.remove()

        # If VM was passed attach/detach disks to/from the VM:
        if (
            module.params.get("vm_id") is not None
            or module.params.get("vm_name") is not None
            and state != "absent"
        ):
            vms_service = connection.system_service().vms_service()

            # If `vm_id` isn't specified, find VM by name:
            vm_id = module.params["vm_id"]
            if vm_id is None:
                vm_id = getattr(
                    search_by_name(vms_service, module.params["vm_name"]), "id", None
                )

            if vm_id is None:
                module.fail_json(msg="VM don't exists, please create it first.")

            disk_attachments_service = vms_service.vm_service(
                vm_id
            ).disk_attachments_service()
            disk_attachments_module = DiskAttachmentsModule(
                connection=connection,
                module=module,
                service=disk_attachments_service,
                changed=ret["changed"] if ret else False,
            )

            if state == "present" or state == "attached":
                ret = disk_attachments_module.create()
                if lun is None:
                    wait(
                        service=disk_attachments_service.service(ret["id"]),
                        condition=lambda d: follow_link(connection, d.disk).status
                        == otypes.DiskStatus.OK,
                        wait=module.params["wait"],
                        timeout=module.params["timeout"],
                    )
            elif state == "detached":
                ret = disk_attachments_module.remove()

        # When the host parameter is specified and the disk is not being
        # removed, refresh the information about the LUN.
        if state != "absent" and host:
            hosts_service = connection.system_service().hosts_service()
            host_id = get_id_by_name(hosts_service, host)
            disks_service.disk_service(disk.id).refresh_lun(otypes.Host(id=host_id))

        module.exit_json(**ret)
    except Exception as e:
        module.fail_json(msg=str(e), exception=traceback.format_exc())
    finally:
        connection.close(logout=auth.get("token") is None)