package org.ovirt.engine.core.vdsbroker;

import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.ovirt.engine.core.common.businessentities.Snapshot;
import org.ovirt.engine.core.common.businessentities.Snapshot.SnapshotStatus;
import org.ovirt.engine.core.common.businessentities.VM;
import org.ovirt.engine.core.common.businessentities.VMStatus;
import org.ovirt.engine.core.common.businessentities.VmDynamic;
import org.ovirt.engine.core.common.vdscommands.CreateVmVDSCommandParameters;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.compat.TransactionScopeOption;
import org.ovirt.engine.core.dal.dbbroker.DbFacade;
import org.ovirt.engine.core.utils.log.Log;
import org.ovirt.engine.core.utils.log.LogFactory;
import org.ovirt.engine.core.utils.transaction.TransactionMethod;
import org.ovirt.engine.core.utils.transaction.TransactionSupport;
import org.ovirt.engine.core.vdsbroker.vdsbroker.CreateVDSCommand;
import org.ovirt.engine.core.vdsbroker.vdsbroker.CreateVmFromCloudInitVDSCommand;
import org.ovirt.engine.core.vdsbroker.vdsbroker.CreateVmFromSysPrepVDSCommand;
import org.ovirt.engine.core.vdsbroker.vdsbroker.CreateVmFromSysPrepVDSCommandParameters;
import org.ovirt.engine.core.vdsbroker.vdsbroker.VDSGenericException;

public class CreateVmVDSCommand<P extends CreateVmVDSCommandParameters> extends VdsIdVDSCommandBase<P> {
    public CreateVmVDSCommand(P parameters) {
        super(parameters);
    }

    @Override
    protected void executeVdsIdCommand() {
        if (_vdsManager == null) {
            getVDSReturnValue().setSucceeded(false);
            return;
        }

        final VM vm = getParameters().getVm();
        if (canExecute() && ResourceManager.getInstance().AddAsyncRunningVm(vm.getId())) {
            CreateVDSCommand<?> command = null;
            try {
                command = initCreateVDSCommand(vm);
                command.execute();
                if (command.getVDSReturnValue().getSucceeded()) {
                    vm.setInitialized(true);
                    saveSetInitializedToDb(vm.getId());

                    TransactionSupport.executeInScope(TransactionScopeOption.Required,
                            new TransactionMethod<Object>() {
                        @Override
                        public Object runInTransaction() {
                            vm.setRunOnVds(getVdsId());
                            if (getParameters().isClearHibernationVolumes()) {
                                vm.setHibernationVolHandle(StringUtils.EMPTY);
                            }
                            DbFacade.getInstance().getVmDynamicDao().update(vm.getDynamicData());
                            return null;
                        }
                    });
                } else {
                    handleCommandResult(command);
                    ResourceManager.getInstance().RemoveAsyncRunningVm(getParameters().getVmId());
                }
            } catch (java.lang.Exception e) {
                log.error("Error in excuting CreateVmVDSCommand", e);
                if (command != null && !command.getVDSReturnValue().getSucceeded()) {
                    ResourceManager.getInstance().RemoveAsyncRunningVm(getParameters().getVmId());
                }
                throw new RuntimeException(e);
            }
        }
        getVDSReturnValue().setReturnValue(vm.getStatus());
    }

    private CreateVDSCommand<?> initCreateVDSCommand(VM vm) throws Exception {
        if (vm.isSysprepUsed()) {
            String domain = (vm.getVmInit() != null && vm.getVmInit().getDomain() != null) ?
                    vm.getVmInit().getDomain() : "";
            // use answer file to run after sysprep.
            CreateVmFromSysPrepVDSCommandParameters createVmFromSysPrepParam =
                    new CreateVmFromSysPrepVDSCommandParameters(
                            getVdsId(),
                            vm,
                            vm.getName(),
                            domain);
            createVmFromSysPrepParam.setSysPrepParams(getParameters().getSysPrepParams());
            return new CreateVmFromSysPrepVDSCommand<CreateVmFromSysPrepVDSCommandParameters>(createVmFromSysPrepParam);
        }
        else if (vm.isCloudInitUsed()) {
            return new CreateVmFromCloudInitVDSCommand<CreateVmVDSCommandParameters>(getParameters());
        }
        else {
            // normal run.
            return new CreateVDSCommand<CreateVmVDSCommandParameters>(getParameters());
        }
    }

    private boolean canExecute() {

        Guid guid = getParameters().getVm().getId();
        String vmName = getParameters().getVm().getName();
        VmDynamic vmDynamicFromDb = DbFacade.getInstance().getVmDynamicDao().get(guid);
        if (ResourceManager.getInstance().IsVmDuringInitiating(getParameters().getVm().getId())) {
            log.infoFormat("Vm Running failed - vm {0}:{1} already running", guid, vmName);
            getVDSReturnValue().setReturnValue(vmDynamicFromDb.getStatus());
            return false;
        } else {
            VMStatus vmStatus = vmDynamicFromDb.getStatus();

            if (vmStatus == VMStatus.ImageLocked) {
                log.infoFormat("Vm Running failed - vm {0}:{1} - cannot run vm when image is locked", guid, vmName);
                return false;
            }
            if (vmDynamicFromDb.getStatus() != VMStatus.Down && vmDynamicFromDb.getStatus() != VMStatus.Suspended) {
                log.infoFormat("Vm Running failed - vm {0}:{1} already running, status {2}", guid, vmName, vmStatus);
                getVDSReturnValue().setReturnValue(vmDynamicFromDb.getStatus());
                return false;
            }

            List<Snapshot> snapshots = DbFacade.getInstance().getSnapshotDao().getAll(guid);

            if (!snapshots.isEmpty() && SnapshotStatus.LOCKED == snapshots.get(snapshots.size() - 1).getStatus()) {
                log.infoFormat("VM Running failed - VM {0}:{1} - cannot run VM when VM during Snapshot", guid, vmName);
                return false;
            }
        }
        return true;
    }

    private void handleCommandResult(CreateVDSCommand<?> command) {
        if (!command.getVDSReturnValue().getSucceeded() && command.getVDSReturnValue().getExceptionObject() != null) {
            if (command.getVDSReturnValue().getExceptionObject() instanceof VDSGenericException) {
                log.errorFormat("VDS::create Failed creating vm '{0}' in vds = {1} : {2} error = {3}",
                        getParameters().getVm().getName(), getVds().getId(), getVds().getName(),
                        command.getVDSReturnValue().getExceptionString());
                getVDSReturnValue().setReturnValue(VMStatus.Down);
                getVDSReturnValue().setSucceeded(false);
                getVDSReturnValue().setExceptionString(command.getVDSReturnValue().getExceptionString());
                getVDSReturnValue().setVdsError(command.getVDSReturnValue().getVdsError());
            } else {
                throw command.getVDSReturnValue().getExceptionObject();
            }
        }
    }

    private void saveSetInitializedToDb(final Guid vmId) {
        DbFacade.getInstance().getVmDao().saveIsInitialized(vmId, true);
    }

    private static Log log = LogFactory.getLog(CreateVmVDSCommand.class);
}

@@ -552,68 +552,8 @@
 id;%0A
-import org.ovirt.engine.core.compat.TransactionScopeOption;%0A
 impo
@@ -699,141 +699,8 @@
 ry;%0A
-import org.ovirt.engine.core.utils.transaction.TransactionMethod;%0Aimport org.ovirt.engine.core.utils.transaction.TransactionSupport;%0A
 impo
@@ -1807,53 +1807,8 @@
 ) %7B%0A
-                    vm.setInitialized(true);%0A
     
@@ -1884,238 +1884,33 @@
     
-TransactionSupport.executeInScope(TransactionScopeOption.Required,%0A                            new TransactionMethod%3CObject%3E() %7B%0A                        @Override%0A                        public Object runInTransaction() %7B%0A        
+vm.setInitialized(true);%0A
     
@@ -1969,32 +1969,24 @@
             
-   
-     
 if (getParam
@@ -2020,32 +2020,24 @@
 olumes()) %7B%0A
-        
             
@@ -2111,37 +2111,26 @@
             
-        %7D%0A   
+%7D%0A
             
@@ -2137,21 +2137,16 @@
         
-     
 DbFacade
@@ -2211,99 +2211,8 @@
 ));%0A
-                            return null;%0A                        %7D%0A                    %7D);%0A
     

remove redundant transaction on run vm