Esempio n. 1
0
  @Override
  @Transactional(readOnly = true)
  public void preRecoverVm(VmInstanceInventory vm) {
    String rootVolUuid = vm.getRootVolumeUuid();

    String sql =
        "select ps.uuid from PrimaryStorageVO ps, VolumeVO vol where ps.uuid = vol.primaryStorageUuid"
            + " and vol.uuid = :uuid and ps.type = :pstype";
    TypedQuery<String> q = dbf.getEntityManager().createQuery(sql, String.class);
    q.setParameter("uuid", rootVolUuid);
    q.setParameter("pstype", LocalStorageConstants.LOCAL_STORAGE_TYPE);
    String psuuid = dbf.find(q);
    if (psuuid == null) {
      return;
    }

    sql =
        "select count(ref) from LocalStorageResourceRefVO ref where ref.resourceUuid = :uuid and ref.resourceType = :rtype";
    TypedQuery<Long> rq = dbf.getEntityManager().createQuery(sql, Long.class);
    rq.setParameter("uuid", rootVolUuid);
    rq.setParameter("rtype", VolumeVO.class.getSimpleName());
    long count = rq.getSingleResult();
    if (count == 0) {
      throw new OperationFailureException(
          errf.stringToOperationError(
              String.format(
                  "unable to recover the vm[uuid:%s, name:%s]. The vm's root volume is on the local"
                      + " storage[uuid:%s]; however, the host on which the root volume is has been deleted",
                  vm.getUuid(), vm.getName(), psuuid)));
    }
  }
Esempio n. 2
0
  @Override
  @Transactional(
      readOnly = true,
      noRollbackForClassName = {"org.zstack.header.errorcode.OperationFailureException"})
  public void preVmMigration(VmInstanceInventory vm) {
    List<String> volUuids =
        CollectionUtils.transformToList(
            vm.getAllVolumes(),
            new Function<String, VolumeInventory>() {
              @Override
              public String call(VolumeInventory arg) {
                return arg.getUuid();
              }
            });

    String sql =
        "select count(ps) from PrimaryStorageVO ps, VolumeVO vol where ps.uuid = vol.primaryStorageUuid and"
            + " vol.uuid in (:volUuids) and ps.type = :ptype";
    TypedQuery<Long> q = dbf.getEntityManager().createQuery(sql, Long.class);
    q.setParameter("volUuids", volUuids);
    q.setParameter("ptype", LocalStorageConstants.LOCAL_STORAGE_TYPE);
    q.setMaxResults(1);
    Long count = q.getSingleResult();
    if (count > 0) {
      throw new OperationFailureException(
          errf.stringToOperationError(
              String.format(
                  "unable to live migrate with local storage. The vm[uuid:%s] has volumes on local storage,"
                      + "to protect your data, please stop the vm and do the volume migration",
                  vm.getUuid())));
    }
  }
Esempio n. 3
0
  @Override
  protected FlowChain getStartVmWorkFlowChain(VmInstanceInventory inv) {
    FlowChain chain = super.getStartVmWorkFlowChain(inv);
    chain.setName(String.format("start-appliancevm-%s", inv.getUuid()));
    chain.insert(
        new Flow() {
          String __name__ = "change-appliancevm-status-to-connecting";
          ApplianceVmStatus originStatus = getSelf().getStatus();

          @Override
          public void run(FlowTrigger trigger, Map data) {
            getSelf().setStatus(ApplianceVmStatus.Connecting);
            self = dbf.updateAndRefresh(self);
            trigger.next();
          }

          @Override
          public void rollback(FlowTrigger trigger, Map data) {
            self = dbf.reload(self);
            getSelf().setStatus(originStatus);
            self = dbf.updateAndRefresh(self);
            trigger.rollback();
          }
        });

    prepareLifeCycleInfo(chain);
    prepareFirewallInfo(chain);

    addBootstrapFlows(chain, HypervisorType.valueOf(inv.getHypervisorType()));

    List<Flow> subStartFlows = getPostStartFlows();
    if (subStartFlows != null) {
      for (Flow f : subStartFlows) {
        chain.then(f);
      }
    }

    chain.then(
        new NoRollbackFlow() {
          String __name__ = "change-appliancevm-status-to-connected";

          @Override
          public void run(FlowTrigger trigger, Map data) {
            getSelf().setStatus(ApplianceVmStatus.Connected);
            self = dbf.updateAndRefresh(self);
            trigger.next();
          }
        });

    boolean noRollbackOnFailure = ApplianceVmGlobalProperty.NO_ROLLBACK_ON_POST_FAILURE;
    chain.noRollback(noRollbackOnFailure);
    return chain;
  }
Esempio n. 4
0
  @Override
  public void preAttachVolume(VmInstanceInventory vm, final VolumeInventory volume) {
    SimpleQuery<LocalStorageResourceRefVO> q = dbf.createQuery(LocalStorageResourceRefVO.class);
    q.add(
        LocalStorageResourceRefVO_.resourceUuid,
        Op.IN,
        list(vm.getRootVolumeUuid(), volume.getUuid()));
    q.groupBy(LocalStorageResourceRefVO_.hostUuid);
    long count = q.count();

    if (count < 2) {
      return;
    }

    q = dbf.createQuery(LocalStorageResourceRefVO.class);
    q.select(LocalStorageResourceRefVO_.hostUuid);
    q.add(LocalStorageResourceRefVO_.resourceUuid, Op.EQ, vm.getRootVolumeUuid());
    String rootHost = q.findValue();

    q = dbf.createQuery(LocalStorageResourceRefVO.class);
    q.select(LocalStorageResourceRefVO_.hostUuid);
    q.add(LocalStorageResourceRefVO_.resourceUuid, Op.EQ, volume.getUuid());
    String dataHost = q.findValue();

    if (!rootHost.equals(dataHost)) {
      throw new OperationFailureException(
          errf.stringToOperationError(
              String.format(
                  "cannot attach the data volume[uuid:%s] to the vm[uuid:%s]. Both vm's root volume and the data volume are"
                      + " on local primary storage, but they are on different hosts. The root volume[uuid:%s] is on the host[uuid:%s] but the data volume[uuid: %s]"
                      + " is on the host[uuid: %s]",
                  volume.getUuid(),
                  vm.getUuid(),
                  vm.getRootVolumeUuid(),
                  rootHost,
                  volume.getUuid(),
                  dataHost)));
    }
  }
Esempio n. 5
0
  @Override
  protected FlowChain getMigrateVmWorkFlowChain(VmInstanceInventory inv) {
    FlowChain chain = super.getMigrateVmWorkFlowChain(inv);
    chain.setName(String.format("migrate-appliancevm-%s", inv.getUuid()));
    prepareLifeCycleInfo(chain);
    prepareFirewallInfo(chain);

    List<Flow> subMigrateFlows = getPostMigrateFlows();
    if (subMigrateFlows != null) {
      for (Flow f : subMigrateFlows) {
        chain.then(f);
      }
    }

    boolean noRollbackOnFailure = ApplianceVmGlobalProperty.NO_ROLLBACK_ON_POST_FAILURE;
    chain.noRollback(noRollbackOnFailure);
    return chain;
  }
Esempio n. 6
0
 private void handle(APIGetDataVolumeAttachableVmMsg msg) {
   APIGetDataVolumeAttachableVmReply reply = new APIGetDataVolumeAttachableVmReply();
   reply.setInventories(
       VmInstanceInventory.valueOf(getCandidateVmForAttaching(msg.getSession().getAccountUuid())));
   bus.reply(msg, reply);
 }
Esempio n. 7
0
  private List<VmInstanceInventory> vmFromDeleteAction(CascadeAction action) {
    List<VmInstanceInventory> ret = null;
    if (HostVO.class.getSimpleName().equals(action.getParentIssuer())) {
      List<HostInventory> hosts = action.getParentIssuerContext();
      List<String> huuids =
          CollectionUtils.transformToList(
              hosts,
              new Function<String, HostInventory>() {
                @Override
                public String call(HostInventory arg) {
                  return arg.getUuid();
                }
              });

      Map<String, VmInstanceVO> vmvos = new HashMap<String, VmInstanceVO>();
      SimpleQuery<VmInstanceVO> q = dbf.createQuery(VmInstanceVO.class);
      q.add(VmInstanceVO_.hostUuid, SimpleQuery.Op.IN, huuids);
      q.add(VmInstanceVO_.type, Op.EQ, VmInstanceConstant.USER_VM_TYPE);
      List<VmInstanceVO> lst = q.list();
      for (VmInstanceVO vo : lst) {
        vmvos.put(vo.getUuid(), vo);
      }

      if (ClusterVO.class.getSimpleName().equals(action.getRootIssuer())) {
        List<ClusterInventory> clusters = action.getRootIssuerContext();
        List<String> clusterUuids =
            CollectionUtils.transformToList(
                clusters,
                new Function<String, ClusterInventory>() {
                  @Override
                  public String call(ClusterInventory arg) {
                    return arg.getUuid();
                  }
                });
        q = dbf.createQuery(VmInstanceVO.class);
        q.add(VmInstanceVO_.clusterUuid, Op.IN, clusterUuids);
        q.add(VmInstanceVO_.type, Op.EQ, VmInstanceConstant.USER_VM_TYPE);
        lst = q.list();
        for (VmInstanceVO vo : lst) {
          vmvos.put(vo.getUuid(), vo);
        }
      } else if (ZoneVO.class.getSimpleName().equals(action.getRootIssuer())) {
        List<ZoneInventory> zones = action.getRootIssuerContext();
        List<String> zoneUuids =
            CollectionUtils.transformToList(
                zones,
                new Function<String, ZoneInventory>() {
                  @Override
                  public String call(ZoneInventory arg) {
                    return arg.getUuid();
                  }
                });
        q = dbf.createQuery(VmInstanceVO.class);
        q.add(VmInstanceVO_.zoneUuid, Op.IN, zoneUuids);
        q.add(VmInstanceVO_.type, Op.EQ, VmInstanceConstant.USER_VM_TYPE);
        lst = q.list();
        for (VmInstanceVO vo : lst) {
          vmvos.put(vo.getUuid(), vo);
        }
      }

      if (!vmvos.isEmpty()) {
        ret = VmInstanceInventory.valueOf(vmvos.values());
      }
    } else if (NAME.equals(action.getParentIssuer())) {
      return action.getParentIssuerContext();
    } else if (PrimaryStorageVO.class.getSimpleName().equals(action.getParentIssuer())) {
      final List<String> pruuids =
          CollectionUtils.transformToList(
              (List<PrimaryStorageInventory>) action.getParentIssuerContext(),
              new Function<String, PrimaryStorageInventory>() {
                @Override
                public String call(PrimaryStorageInventory arg) {
                  return arg.getUuid();
                }
              });

      List<VmInstanceVO> vmvos =
          new Callable<List<VmInstanceVO>>() {
            @Override
            @Transactional(readOnly = true)
            public List<VmInstanceVO> call() {
              String sql =
                  "select vm from VmInstanceVO vm, VolumeVO vol, PrimaryStorageVO pr where vm.type = :vmType and vm.uuid = vol.vmInstanceUuid"
                      + " and vol.primaryStorageUuid = pr.uuid and vol.type = :volType and pr.uuid in (:uuids) group by vm.uuid";
              TypedQuery<VmInstanceVO> q =
                  dbf.getEntityManager().createQuery(sql, VmInstanceVO.class);
              q.setParameter("vmType", VmInstanceConstant.USER_VM_TYPE);
              q.setParameter("uuids", pruuids);
              q.setParameter("volType", VolumeType.Root);
              return q.getResultList();
            }
          }.call();

      if (!vmvos.isEmpty()) {
        ret = VmInstanceInventory.valueOf(vmvos);
      }
    } else if (L3NetworkVO.class.getSimpleName().equals(action.getParentIssuer())) {
      final List<String> l3uuids =
          CollectionUtils.transformToList(
              (List<L3NetworkInventory>) action.getParentIssuerContext(),
              new Function<String, L3NetworkInventory>() {
                @Override
                public String call(L3NetworkInventory arg) {
                  return arg.getUuid();
                }
              });

      List<VmInstanceVO> vmvos =
          new Callable<List<VmInstanceVO>>() {
            @Override
            @Transactional(readOnly = true)
            public List<VmInstanceVO> call() {
              String sql =
                  "select vm from VmInstanceVO vm, L3NetworkVO l3, VmNicVO nic where vm.type = :vmType and vm.uuid = nic.vmInstanceUuid and vm.state not in (:vmStates)"
                      + " and nic.l3NetworkUuid = l3.uuid and l3.uuid in (:uuids) group by vm.uuid";
              TypedQuery<VmInstanceVO> q =
                  dbf.getEntityManager().createQuery(sql, VmInstanceVO.class);
              q.setParameter("vmType", VmInstanceConstant.USER_VM_TYPE);
              q.setParameter(
                  "vmStates", Arrays.asList(VmInstanceState.Stopped, VmInstanceState.Stopping));
              q.setParameter("uuids", l3uuids);
              return q.getResultList();
            }
          }.call();

      if (!vmvos.isEmpty()) {
        ret = VmInstanceInventory.valueOf(vmvos);
      }
    } else if (IpRangeVO.class.getSimpleName().equals(action.getParentIssuer())) {
      final List<String> ipruuids =
          CollectionUtils.transformToList(
              (List<IpRangeInventory>) action.getParentIssuerContext(),
              new Function<String, IpRangeInventory>() {
                @Override
                public String call(IpRangeInventory arg) {
                  return arg.getUuid();
                }
              });

      List<VmInstanceVO> vmvos =
          new Callable<List<VmInstanceVO>>() {
            @Override
            @Transactional(readOnly = true)
            public List<VmInstanceVO> call() {
              String sql =
                  "select vm from VmInstanceVO vm, VmNicVO nic, UsedIpVO ip, IpRangeVO ipr where vm.type = :vmType and vm.uuid = nic.vmInstanceUuid and vm.state not in (:vmStates)"
                      + " and nic.usedIpUuid = ip.uuid and ip.ipRangeUuid = ipr.uuid and ipr.uuid in (:uuids) group by vm.uuid";
              TypedQuery<VmInstanceVO> q =
                  dbf.getEntityManager().createQuery(sql, VmInstanceVO.class);
              q.setParameter("vmType", VmInstanceConstant.USER_VM_TYPE);
              q.setParameter(
                  "vmStates", Arrays.asList(VmInstanceState.Stopped, VmInstanceState.Stopping));
              q.setParameter("uuids", ipruuids);
              return q.getResultList();
            }
          }.call();

      if (!vmvos.isEmpty()) {
        ret = VmInstanceInventory.valueOf(vmvos);
      }
    } else if (AccountVO.class.getSimpleName().equals(action.getParentIssuer())) {
      final List<String> auuids =
          CollectionUtils.transformToList(
              (List<AccountInventory>) action.getParentIssuerContext(),
              new Function<String, AccountInventory>() {
                @Override
                public String call(AccountInventory arg) {
                  return arg.getUuid();
                }
              });

      List<VmInstanceVO> vmvos =
          new Callable<List<VmInstanceVO>>() {
            @Override
            @Transactional(readOnly = true)
            public List<VmInstanceVO> call() {
              String sql =
                  "select d from VmInstanceVO d, AccountResourceRefVO r where d.uuid = r.resourceUuid and"
                      + " r.resourceType = :rtype and r.accountUuid in (:auuids) group by d.uuid";
              TypedQuery<VmInstanceVO> q =
                  dbf.getEntityManager().createQuery(sql, VmInstanceVO.class);
              q.setParameter("rtype", VmInstanceVO.class.getSimpleName());
              q.setParameter("auuids", auuids);
              return q.getResultList();
            }
          }.call();

      if (!vmvos.isEmpty()) {
        ret = VmInstanceInventory.valueOf(vmvos);
      }
    }

    return ret;
  }
Esempio n. 8
0
  private void handleDeletion(final CascadeAction action, final Completion completion) {
    int op = toDeletionOpCode(action);
    if (op == OP_NOPE) {
      completion.success();
      return;
    }

    if (op == OP_REMOVE_INSTANCE_OFFERING) {
      if (VmGlobalConfig.UPDATE_INSTANCE_OFFERING_TO_NULL_WHEN_DELETING.value(Boolean.class)) {
        new Runnable() {
          @Override
          @Transactional
          public void run() {
            List<InstanceOfferingInventory> offerings = action.getParentIssuerContext();
            List<String> offeringUuids =
                CollectionUtils.transformToList(
                    offerings,
                    new Function<String, InstanceOfferingInventory>() {
                      @Override
                      public String call(InstanceOfferingInventory arg) {
                        return arg.getUuid();
                      }
                    });

            String sql =
                "update VmInstanceVO vm set vm.instanceOfferingUuid = null where vm.instanceOfferingUuid in (:offeringUuids)";
            Query q = dbf.getEntityManager().createQuery(sql);
            q.setParameter("offeringUuids", offeringUuids);
            q.executeUpdate();
          }
        }.run();
      }

      completion.success();
      return;
    }

    final List<VmInstanceInventory> vminvs = vmFromDeleteAction(action);
    if (vminvs == null) {
      completion.success();
      return;
    }

    if (op == OP_STOP) {
      List<StopVmInstanceMsg> msgs = new ArrayList<StopVmInstanceMsg>();
      for (VmInstanceInventory inv : vminvs) {
        StopVmInstanceMsg msg = new StopVmInstanceMsg();
        msg.setVmInstanceUuid(inv.getUuid());
        bus.makeTargetServiceIdByResourceUuid(msg, VmInstanceConstant.SERVICE_ID, inv.getUuid());
        msgs.add(msg);
      }

      bus.send(
          msgs,
          20,
          new CloudBusListCallBack(completion) {
            @Override
            public void run(List<MessageReply> replies) {
              if (!action.isActionCode(CascadeConstant.DELETION_FORCE_DELETE_CODE)) {
                for (MessageReply r : replies) {
                  if (!r.isSuccess()) {
                    completion.fail(r.getError());
                    return;
                  }
                }
              }

              completion.success();
            }
          });
    } else if (op == OP_DELETION) {
      List<VmInstanceDeletionMsg> msgs = new ArrayList<VmInstanceDeletionMsg>();
      for (VmInstanceInventory inv : vminvs) {
        VmInstanceDeletionMsg msg = new VmInstanceDeletionMsg();
        msg.setForceDelete(action.isActionCode(CascadeConstant.DELETION_FORCE_DELETE_CODE));
        msg.setVmInstanceUuid(inv.getUuid());
        bus.makeTargetServiceIdByResourceUuid(msg, VmInstanceConstant.SERVICE_ID, inv.getUuid());
        msgs.add(msg);
      }

      bus.send(
          msgs,
          20,
          new CloudBusListCallBack(completion) {
            @Override
            public void run(List<MessageReply> replies) {
              if (!action.isActionCode(CascadeConstant.DELETION_FORCE_DELETE_CODE)) {
                for (MessageReply r : replies) {
                  if (!r.isSuccess()) {
                    completion.fail(r.getError());
                    return;
                  }
                }
              }

              completion.success();
            }
          });
    } else if (op == OP_DETACH_NIC) {
      List<DetachNicFromVmMsg> msgs = new ArrayList<DetachNicFromVmMsg>();
      List<L3NetworkInventory> l3s = action.getParentIssuerContext();
      for (VmInstanceInventory vm : vminvs) {
        for (L3NetworkInventory l3 : l3s) {
          DetachNicFromVmMsg msg = new DetachNicFromVmMsg();
          msg.setVmInstanceUuid(vm.getUuid());
          msg.setVmNicUuid(vm.findNic(l3.getUuid()).getUuid());
          bus.makeTargetServiceIdByResourceUuid(msg, VmInstanceConstant.SERVICE_ID, vm.getUuid());
          msgs.add(msg);
        }
      }

      bus.send(
          msgs,
          new CloudBusListCallBack(completion) {
            @Override
            public void run(List<MessageReply> replies) {
              if (!action.isActionCode(CascadeConstant.DELETION_FORCE_DELETE_CODE)) {
                for (MessageReply r : replies) {
                  if (!r.isSuccess()) {
                    completion.fail(r.getError());
                    return;
                  }
                }
              }

              completion.success();
            }
          });
    }
  }
Esempio n. 9
0
  @Override
  @Transactional(readOnly = true)
  public List<VolumeVO> returnAttachableVolumes(VmInstanceInventory vm, List<VolumeVO> candidates) {
    // find instantiated volumes
    List<String> volUuids =
        CollectionUtils.transformToList(
            candidates,
            new Function<String, VolumeVO>() {
              @Override
              public String call(VolumeVO arg) {
                return VolumeStatus.Ready == arg.getStatus() ? arg.getUuid() : null;
              }
            });

    if (volUuids.isEmpty()) {
      return candidates;
    }

    List<VolumeVO> uninstantiatedVolumes =
        CollectionUtils.transformToList(
            candidates,
            new Function<VolumeVO, VolumeVO>() {
              @Override
              public VolumeVO call(VolumeVO arg) {
                return arg.getStatus() == VolumeStatus.NotInstantiated ? arg : null;
              }
            });

    String sql =
        "select ref.hostUuid from LocalStorageResourceRefVO ref where ref.resourceUuid = :volUuid and ref.resourceType = :rtype";
    TypedQuery<String> q = dbf.getEntityManager().createQuery(sql, String.class);
    q.setParameter("volUuid", vm.getRootVolumeUuid());
    q.setParameter("rtype", VolumeVO.class.getSimpleName());
    List<String> ret = q.getResultList();
    if (ret.isEmpty()) {
      return candidates;
    }

    String hostUuid = ret.get(0);
    sql =
        "select ref.resourceUuid from LocalStorageResourceRefVO ref where ref.resourceUuid in (:uuids) and ref.resourceType = :rtype"
            + " and ref.hostUuid != :huuid";
    q = dbf.getEntityManager().createQuery(sql, String.class);
    q.setParameter("uuids", volUuids);
    q.setParameter("huuid", hostUuid);
    q.setParameter("rtype", VolumeVO.class.getSimpleName());
    final List<String> toExclude = q.getResultList();

    candidates =
        CollectionUtils.transformToList(
            candidates,
            new Function<VolumeVO, VolumeVO>() {
              @Override
              public VolumeVO call(VolumeVO arg) {
                return toExclude.contains(arg.getUuid()) ? null : arg;
              }
            });

    candidates.addAll(uninstantiatedVolumes);

    return candidates;
  }
Esempio n. 10
0
  @Override
  protected void startVmFromNewCreate(
      final StartNewCreatedVmInstanceMsg msg, final SyncTaskChain taskChain) {
    boolean callNext = true;
    try {
      refreshVO();
      ErrorCode allowed = validateOperationByState(msg, self.getState(), null);
      if (allowed != null) {
        bus.replyErrorByMessageType(msg, allowed);
        return;
      }
      ErrorCode preCreated = extEmitter.preStartNewCreatedVm(msg.getVmInstanceInventory());
      if (preCreated != null) {
        bus.replyErrorByMessageType(msg, preCreated);
        return;
      }

      StartNewCreatedApplianceVmMsg smsg = (StartNewCreatedApplianceVmMsg) msg;
      ApplianceVmSpec aspec = smsg.getApplianceVmSpec();

      final VmInstanceSpec spec = new VmInstanceSpec();
      spec.setVmInventory(msg.getVmInstanceInventory());
      if (msg.getL3NetworkUuids() != null && !msg.getL3NetworkUuids().isEmpty()) {
        SimpleQuery<L3NetworkVO> nwquery = dbf.createQuery(L3NetworkVO.class);
        nwquery.add(L3NetworkVO_.uuid, SimpleQuery.Op.IN, msg.getL3NetworkUuids());
        List<L3NetworkVO> vos = nwquery.list();
        List<L3NetworkInventory> nws = L3NetworkInventory.valueOf(vos);
        spec.setL3Networks(nws);
      } else {
        spec.setL3Networks(new ArrayList<L3NetworkInventory>(0));
      }

      if (msg.getDataDiskOfferingUuids() != null && !msg.getDataDiskOfferingUuids().isEmpty()) {
        SimpleQuery<DiskOfferingVO> dquery = dbf.createQuery(DiskOfferingVO.class);
        dquery.add(DiskOfferingVO_.uuid, SimpleQuery.Op.IN, msg.getDataDiskOfferingUuids());
        List<DiskOfferingVO> vos = dquery.list();

        // allow create multiple data volume from the same disk offering
        List<DiskOfferingInventory> disks = new ArrayList<DiskOfferingInventory>();
        for (final String duuid : msg.getDataDiskOfferingUuids()) {
          DiskOfferingVO dvo =
              CollectionUtils.find(
                  vos,
                  new Function<DiskOfferingVO, DiskOfferingVO>() {
                    @Override
                    public DiskOfferingVO call(DiskOfferingVO arg) {
                      if (duuid.equals(arg.getUuid())) {
                        return arg;
                      }
                      return null;
                    }
                  });
          disks.add(DiskOfferingInventory.valueOf(dvo));
        }
        spec.setDataDiskOfferings(disks);
      } else {
        spec.setDataDiskOfferings(new ArrayList<DiskOfferingInventory>(0));
      }

      ImageVO imvo = dbf.findByUuid(spec.getVmInventory().getImageUuid(), ImageVO.class);
      spec.getImageSpec().setInventory(ImageInventory.valueOf(imvo));

      spec.putExtensionData(ApplianceVmConstant.Params.applianceVmSpec.toString(), aspec);
      spec.setCurrentVmOperation(VmInstanceConstant.VmOperation.NewCreate);
      spec.putExtensionData(
          ApplianceVmConstant.Params.applianceVmSubType.toString(), getSelf().getApplianceVmType());

      changeVmStateInDb(VmInstanceStateEvent.starting);

      extEmitter.beforeStartNewCreatedVm(VmInstanceInventory.valueOf(self));
      FlowChain chain = apvmf.getCreateApplianceVmWorkFlowBuilder().build();
      chain.setName(String.format("create-appliancevm-%s", msg.getVmInstanceUuid()));
      chain.getData().put(VmInstanceConstant.Params.VmInstanceSpec.toString(), spec);
      chain
          .getData()
          .put(
              ApplianceVmConstant.Params.applianceVmFirewallRules.toString(),
              aspec.getFirewallRules());

      addBootstrapFlows(
          chain, VolumeFormat.getMasterHypervisorTypeByVolumeFormat(imvo.getFormat()));

      List<Flow> subCreateFlows = getPostCreateFlows();
      if (subCreateFlows != null) {
        for (Flow f : subCreateFlows) {
          chain.then(f);
        }
      }

      chain.then(
          new NoRollbackFlow() {
            String __name__ = "change-appliancevm-status-to-connected";

            @Override
            public void run(FlowTrigger trigger, Map data) {
              // must reload here, otherwise it will override changes created by previous flows
              self = dbf.reload(self);
              getSelf().setStatus(ApplianceVmStatus.Connected);
              dbf.update(self);
              trigger.next();
            }
          });

      boolean noRollbackOnFailure = ApplianceVmGlobalProperty.NO_ROLLBACK_ON_POST_FAILURE;
      chain.noRollback(noRollbackOnFailure);
      chain
          .done(
              new FlowDoneHandler(msg, taskChain) {
                @Override
                public void handle(Map data) {
                  VmInstanceSpec spec =
                      (VmInstanceSpec)
                          data.get(VmInstanceConstant.Params.VmInstanceSpec.toString());
                  self = dbf.reload(self);
                  self.setLastHostUuid(spec.getDestHost().getUuid());
                  self.setHostUuid(spec.getDestHost().getUuid());
                  self.setClusterUuid(spec.getDestHost().getClusterUuid());
                  self.setZoneUuid(spec.getDestHost().getZoneUuid());
                  self.setHypervisorType(spec.getDestHost().getHypervisorType());
                  self.setRootVolumeUuid(spec.getDestRootVolume().getUuid());
                  changeVmStateInDb(VmInstanceStateEvent.running);
                  logger.debug(
                      String.format(
                          "appliance vm[uuid:%s, name: %s, type:%s] is running ..",
                          self.getUuid(), self.getName(), getSelf().getApplianceVmType()));
                  VmInstanceInventory inv = VmInstanceInventory.valueOf(self);
                  extEmitter.afterStartNewCreatedVm(inv);
                  StartNewCreatedVmInstanceReply reply = new StartNewCreatedVmInstanceReply();
                  reply.setVmInventory(inv);
                  bus.reply(msg, reply);
                  taskChain.next();
                }
              })
          .error(
              new FlowErrorHandler(msg, taskChain) {
                @Override
                public void handle(ErrorCode errCode, Map data) {
                  extEmitter.failedToStartNewCreatedVm(VmInstanceInventory.valueOf(self), errCode);
                  dbf.remove(self);
                  StartNewCreatedVmInstanceReply reply = new StartNewCreatedVmInstanceReply();
                  reply.setError(errCode);
                  reply.setSuccess(false);
                  bus.reply(msg, reply);
                  taskChain.next();
                }
              })
          .start();

      callNext = false;
    } finally {
      if (callNext) {
        taskChain.next();
      }
    }
  }