<commit_before>package org.ovirt.engine.core.bll;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.ovirt.engine.core.common.VdcObjectType;
import org.ovirt.engine.core.common.action.RemoveImageParameters;
import org.ovirt.engine.core.common.action.VdcActionType;
import org.ovirt.engine.core.common.asynctasks.AsyncTaskCreationInfo;
import org.ovirt.engine.core.common.asynctasks.AsyncTaskParameters;
import org.ovirt.engine.core.common.asynctasks.AsyncTaskType;
import org.ovirt.engine.core.common.businessentities.AsyncTaskResultEnum;
import org.ovirt.engine.core.common.businessentities.AsyncTaskStatusEnum;
import org.ovirt.engine.core.common.businessentities.DiskImage;
import org.ovirt.engine.core.common.businessentities.Snapshot;
import org.ovirt.engine.core.common.businessentities.VM;
import org.ovirt.engine.core.common.businessentities.VmDeviceId;
import org.ovirt.engine.core.common.businessentities.VmNetworkInterface;
import org.ovirt.engine.core.common.businessentities.async_tasks;
import org.ovirt.engine.core.common.businessentities.image_storage_domain_map_id;
import org.ovirt.engine.core.common.errors.VdcBLLException;
import org.ovirt.engine.core.common.vdscommands.DeleteImageGroupVDSCommandParameters;
import org.ovirt.engine.core.common.vdscommands.VDSCommandType;
import org.ovirt.engine.core.common.vdscommands.VDSReturnValue;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.compat.TransactionScopeOption;
import org.ovirt.engine.core.dao.VmDeviceDAO;
import org.ovirt.engine.core.utils.ovf.OvfManager;
import org.ovirt.engine.core.utils.ovf.OvfReaderException;
import org.ovirt.engine.core.utils.transaction.TransactionMethod;
import org.ovirt.engine.core.utils.transaction.TransactionSupport;

/**
 * This command responsible to removing image, contains all created snapshots.
 */
@SuppressWarnings("serial")
@InternalCommandAttribute
@NonTransactiveCommandAttribute
public class RemoveImageCommand<T extends RemoveImageParameters> extends BaseImagesCommand<T> {

    public RemoveImageCommand(T parameters) {
        super(parameters);
        initImage();
        initStoragePoolId();
        initStorageDomainId();
    }

    protected void initImage() {
        setDiskImage(((getParameters().getDiskImage()) != null) ? getParameters().getDiskImage() : getImage());
    }

    protected void initStoragePoolId() {
        if (getStoragePoolId() == null || Guid.Empty.equals(getStoragePoolId())) {
            setStoragePoolId(getDiskImage() != null && getDiskImage().getstorage_pool_id() != null ? getDiskImage()
                    .getstorage_pool_id().getValue() : Guid.Empty);
        }
    }

    protected void initStorageDomainId() {
        if ((getParameters().getStorageDomainId() == null || Guid.Empty.equals(getParameters().getStorageDomainId()))
                && getDiskImage() != null) {
            setStorageDomainId(getDiskImage().getstorage_ids().get(0));
        }
    }

    @Override
    protected void executeCommand() {
        if (getDiskImage() != null) {
            VDSReturnValue vdsReturnValue = performImageVdsmOperation();
            getReturnValue().getInternalTaskIdList().add(
                    CreateTask(vdsReturnValue.getCreationInfo(),
                            getParameters().getParentCommand(),
                            VdcObjectType.Storage,
                            getParameters().getStorageDomainId()));

            if (getParameters().getParentCommand() != VdcActionType.RemoveVmFromImportExport
                    && getParameters().getParentCommand() != VdcActionType.RemoveVmTemplateFromImportExport
                    && getParameters().getParentCommand() != VdcActionType.RemoveDisk) {
                removeImageFromDB();
            }
        } else {
            log.warn("DiskImage is null, nothing to remove");
        }
        setSucceeded(true);
    }

    @Override
    protected SPMAsyncTask ConcreteCreateTask(AsyncTaskCreationInfo asyncTaskCreationInfo, VdcActionType parentCommand) {
        AsyncTaskParameters p = new AsyncTaskParameters(asyncTaskCreationInfo, new async_tasks(parentCommand,
                AsyncTaskResultEnum.success, AsyncTaskStatusEnum.running, asyncTaskCreationInfo.getTaskID(),
                getParametersForTask(parentCommand, getParameters()), asyncTaskCreationInfo.getStepId(),
                getCommandId()));
        p.setEntityId(getParameters().getEntityId());
        return AsyncTaskManager.getInstance().CreateTask(AsyncTaskType.deleteImage, p);
    }

    private void removeImageFromDB() {
        final DiskImage diskImage = getDiskImage();

        final List<Snapshot> updatedSnapshots = prepareSnapshotConfigWithoutImage(diskImage.getId());
        TransactionSupport.executeInScope(TransactionScopeOption.Required,
                new TransactionMethod<Object>() {
                    @Override
                    public Object runInTransaction() {
                        getDiskImageDynamicDAO().remove(diskImage.getImageId());
                        Guid imageTemplate = diskImage.getit_guid();
                        Guid currentGuid = diskImage.getImageId();
                        // next 'while' statement removes snapshots from DB only (the
                        // 'DeleteImageGroup'
                        // VDS Command should take care of removing all the snapshots from
                        // the storage).
                        while (!currentGuid.equals(imageTemplate) && !currentGuid.equals(Guid.Empty)) {
                            RemoveChildren(currentGuid);

                            DiskImage image = getDiskImageDao().getSnapshotById(currentGuid);
                            if (image != null) {
                                RemoveSnapshot(image);
                                currentGuid = image.getParentId();
                            } else {
                                currentGuid = Guid.Empty;
                                log.warnFormat(
                                        "'image' (snapshot of image '{0}') is null, cannot remove it.",
                                        diskImage.getImageId());
                            }
                        }

                        getBaseDiskDao().remove(diskImage.getId());
                        getVmDeviceDAO().remove(new VmDeviceId(diskImage.getId(), null));
                        for (Snapshot s : updatedSnapshots) {
                            getSnapshotDao().update(s);
                        }

                        return null;
                    }
                });
    }

    private void GetImageChildren(Guid snapshot, List<Guid> children) {
        List<Guid> list = new ArrayList<Guid>();
        for (DiskImage image : getDiskImageDAO().getAllSnapshotsForParent(snapshot)) {
            list.add(image.getImageId());
        }
        children.addAll(list);
        for (Guid snapshotId : list) {
            GetImageChildren(snapshotId, children);
        }
    }

    private void RemoveChildren(Guid snapshot) {
        List<Guid> children = new ArrayList<Guid>();
        GetImageChildren(snapshot, children);
        Collections.reverse(children);
        for (Guid child : children) {
            RemoveSnapshot(getDiskImageDao().getSnapshotById(child));
        }
    }

    /**
     * Prepare a {@link List} of {@link Snapshot} objects with the given disk (image group) removed from it.
     */
    private List<Snapshot> prepareSnapshotConfigWithoutImage(Guid imageGroupToRemove) {
        List<Snapshot> result = new LinkedList<Snapshot>();
        List<DiskImage> snapshotDisks = getDiskImageDao().getAllSnapshotsForImageGroup(imageGroupToRemove);
        for (DiskImage snapshotDisk : snapshotDisks) {
            Snapshot updated =
                    prepareSnapshotConfigWithoutImageSingleImage(snapshotDisk.getvm_snapshot_id().getValue(),
                            snapshotDisk.getImageId());
            if (updated != null) {
                result.add(updated);
            }
        }

        return result;
    }

    /**
     * Prepare a single {@link Snapshot} object representing a snapshot of a given VM without the give disk.
     */
    protected Snapshot prepareSnapshotConfigWithoutImageSingleImage(Guid vmSnapshotId, Guid imageId) {
        Snapshot snap = null;
        try {
            OvfManager ovfManager = new OvfManager();
            snap = getSnapshotDao().get(vmSnapshotId);<commit_msg>core: fixed NPE during removal of floating disk (#845046)<commit_after>package org.ovirt.engine.core.bll;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.ovirt.engine.core.common.VdcObjectType;
import org.ovirt.engine.core.common.action.RemoveImageParameters;
import org.ovirt.engine.core.common.action.VdcActionType;
import org.ovirt.engine.core.common.asynctasks.AsyncTaskCreationInfo;
import org.ovirt.engine.core.common.asynctasks.AsyncTaskParameters;
import org.ovirt.engine.core.common.asynctasks.AsyncTaskType;
import org.ovirt.engine.core.common.businessentities.AsyncTaskResultEnum;
import org.ovirt.engine.core.common.businessentities.AsyncTaskStatusEnum;
import org.ovirt.engine.core.common.businessentities.DiskImage;
import org.ovirt.engine.core.common.businessentities.Snapshot;
import org.ovirt.engine.core.common.businessentities.VM;
import org.ovirt.engine.core.common.businessentities.VmDeviceId;
import org.ovirt.engine.core.common.businessentities.VmNetworkInterface;
import org.ovirt.engine.core.common.businessentities.async_tasks;
import org.ovirt.engine.core.common.businessentities.image_storage_domain_map_id;
import org.ovirt.engine.core.common.errors.VdcBLLException;
import org.ovirt.engine.core.common.vdscommands.DeleteImageGroupVDSCommandParameters;
import org.ovirt.engine.core.common.vdscommands.VDSCommandType;
import org.ovirt.engine.core.common.vdscommands.VDSReturnValue;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.compat.NGuid;
import org.ovirt.engine.core.compat.TransactionScopeOption;
import org.ovirt.engine.core.dao.VmDeviceDAO;
import org.ovirt.engine.core.utils.ovf.OvfManager;
import org.ovirt.engine.core.utils.ovf.OvfReaderException;
import org.ovirt.engine.core.utils.transaction.TransactionMethod;
import org.ovirt.engine.core.utils.transaction.TransactionSupport;

/**
 * This command responsible to removing image, contains all created snapshots.
 */
@SuppressWarnings("serial")
@InternalCommandAttribute
@NonTransactiveCommandAttribute
public class RemoveImageCommand<T extends RemoveImageParameters> extends BaseImagesCommand<T> {

    public RemoveImageCommand(T parameters) {
        super(parameters);
        initImage();
        initStoragePoolId();
        initStorageDomainId();
    }

    protected void initImage() {
        setDiskImage(((getParameters().getDiskImage()) != null) ? getParameters().getDiskImage() : getImage());
    }

    protected void initStoragePoolId() {
        if (getStoragePoolId() == null || Guid.Empty.equals(getStoragePoolId())) {
            setStoragePoolId(getDiskImage() != null && getDiskImage().getstorage_pool_id() != null ? getDiskImage()
                    .getstorage_pool_id().getValue() : Guid.Empty);
        }
    }

    protected void initStorageDomainId() {
        if ((getParameters().getStorageDomainId() == null || Guid.Empty.equals(getParameters().getStorageDomainId()))
                && getDiskImage() != null) {
            setStorageDomainId(getDiskImage().getstorage_ids().get(0));
        }
    }

    @Override
    protected void executeCommand() {
        if (getDiskImage() != null) {
            VDSReturnValue vdsReturnValue = performImageVdsmOperation();
            getReturnValue().getInternalTaskIdList().add(
                    CreateTask(vdsReturnValue.getCreationInfo(),
                            getParameters().getParentCommand(),
                            VdcObjectType.Storage,
                            getParameters().getStorageDomainId()));

            if (getParameters().getParentCommand() != VdcActionType.RemoveVmFromImportExport
                    && getParameters().getParentCommand() != VdcActionType.RemoveVmTemplateFromImportExport
                    && getParameters().getParentCommand() != VdcActionType.RemoveDisk) {
                removeImageFromDB();
            }
        } else {
            log.warn("DiskImage is null, nothing to remove");
        }
        setSucceeded(true);
    }

    @Override
    protected SPMAsyncTask ConcreteCreateTask(AsyncTaskCreationInfo asyncTaskCreationInfo, VdcActionType parentCommand) {
        AsyncTaskParameters p = new AsyncTaskParameters(asyncTaskCreationInfo, new async_tasks(parentCommand,
                AsyncTaskResultEnum.success, AsyncTaskStatusEnum.running, asyncTaskCreationInfo.getTaskID(),
                getParametersForTask(parentCommand, getParameters()), asyncTaskCreationInfo.getStepId(),
                getCommandId()));
        p.setEntityId(getParameters().getEntityId());
        return AsyncTaskManager.getInstance().CreateTask(AsyncTaskType.deleteImage, p);
    }

    private void removeImageFromDB() {
        final DiskImage diskImage = getDiskImage();

        final List<Snapshot> updatedSnapshots = prepareSnapshotConfigWithoutImage(diskImage.getId());
        TransactionSupport.executeInScope(TransactionScopeOption.Required,
                new TransactionMethod<Object>() {
                    @Override
                    public Object runInTransaction() {
                        getDiskImageDynamicDAO().remove(diskImage.getImageId());
                        Guid imageTemplate = diskImage.getit_guid();
                        Guid currentGuid = diskImage.getImageId();
                        // next 'while' statement removes snapshots from DB only (the
                        // 'DeleteImageGroup'
                        // VDS Command should take care of removing all the snapshots from
                        // the storage).
                        while (!currentGuid.equals(imageTemplate) && !currentGuid.equals(Guid.Empty)) {
                            RemoveChildren(currentGuid);

                            DiskImage image = getDiskImageDao().getSnapshotById(currentGuid);
                            if (image != null) {
                                RemoveSnapshot(image);
                                currentGuid = image.getParentId();
                            } else {
                                currentGuid = Guid.Empty;
                                log.warnFormat(
                                        "'image' (snapshot of image '{0}') is null, cannot remove it.",
                                        diskImage.getImageId());
                            }
                        }

                        getBaseDiskDao().remove(diskImage.getId());
                        getVmDeviceDAO().remove(new VmDeviceId(diskImage.getId(), null));
                        for (Snapshot s : updatedSnapshots) {
                            getSnapshotDao().update(s);
                        }

                        return null;
                    }
                });
    }

    private void GetImageChildren(Guid snapshot, List<Guid> children) {
        List<Guid> list = new ArrayList<Guid>();
        for (DiskImage image : getDiskImageDAO().getAllSnapshotsForParent(snapshot)) {
            list.add(image.getImageId());
        }
        children.addAll(list);
        for (Guid snapshotId : list) {
            GetImageChildren(snapshotId, children);
        }
    }

    private void RemoveChildren(Guid snapshot) {
        List<Guid> children = new ArrayList<Guid>();
        GetImageChildren(snapshot, children);
        Collections.reverse(children);
        for (Guid child : children) {
            RemoveSnapshot(getDiskImageDao().getSnapshotById(child));
        }
    }

    /**
     * Prepare a {@link List} of {@link Snapshot} objects with the given disk (image group) removed from it.
     */
    private List<Snapshot> prepareSnapshotConfigWithoutImage(Guid imageGroupToRemove) {
        List<Snapshot> result = new LinkedList<Snapshot>();
        List<DiskImage> snapshotDisks = getDiskImageDao().getAllSnapshotsForImageGroup(imageGroupToRemove);
        for (DiskImage snapshotDisk : snapshotDisks) {
            NGuid vmSnapshotId = snapshotDisk.getvm_snapshot_id();
            if (vmSnapshotId != null && !Guid.Empty.equals(vmSnapshotId.getValue())) {
                Snapshot updated =
                        prepareSnapshotConfigWithoutImageSingleImage(vmSnapshotId.getValue(),
                                snapshotDisk.getImageId());
                if (updated != null) {
                    result.add(updated);
                }
            }
        }

        return result;
    }

    /**
     * Prepare a single {@link Snapshot} object representing a snapshot of a given VM without the give disk.
     */
    protected Snapshot prepareSnapshotConfigWithoutImageSingleImage(Guid vmSnapshotId, Guid imageId) {
        Snapshot snap = null;
        try {
            OvfManager ovfManager = new OvfManager();
            snap = getSnapshotDao().get(vmSnapshotId);
{'fraction_non_alphanumeric': 0.09814186273945809, 'fraction_numerical': 0.0005752746936662257, 'mean_word_length': 2.4526315789473685, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6725132', 'n_tokens_mistral': 4420, 'n_tokens_neox': 4331, 'n_words': 772}