@Override
  public Flow marshalVmOperationFlow(
      String previousFlowName, String nextFlowName, FlowChain chain, VmInstanceSpec spec) {
    if (VmAllocatePrimaryStorageFlow.class.getName().equals(nextFlowName)) {
      if (spec.getCurrentVmOperation() == VmOperation.NewCreate) {
        if (getLocalStorageInCluster(spec.getDestHost().getClusterUuid()) != null) {
          return new LocalStorageAllocateCapacityFlow();
        }
      }
    } else if (spec.getCurrentVmOperation() == VmOperation.AttachVolume) {
      VolumeInventory volume = spec.getDestDataVolumes().get(0);
      if (VolumeStatus.NotInstantiated.toString().equals(volume.getStatus())
          && VmAllocatePrimaryStorageForAttachingDiskFlow.class.getName().equals(nextFlowName)) {
        if (isRootVolumeOnLocalStorage(spec.getVmInventory().getRootVolumeUuid())) {
          return new LocalStorageAllocateCapacityForAttachingVolumeFlow();
        }
      }
    } else if (spec.getCurrentVmOperation() == VmOperation.Migrate
        && isRootVolumeOnLocalStorage(spec.getVmInventory().getRootVolumeUuid())
        && VmMigrateOnHypervisorFlow.class.getName().equals(nextFlowName)) {
      if (KVMConstant.KVM_HYPERVISOR_TYPE.equals(spec.getVmInventory().getHypervisorType())) {
        return new LocalStorageKvmMigrateVmFlow();
      } else {
        throw new OperationFailureException(
            errf.stringToOperationError(
                String.format(
                    "local storage doesn't support live migration for hypervisor[%s]",
                    spec.getVmInventory().getHypervisorType())));
      }
    }

    return null;
  }
  private void releaseNetworkServices(
      final VmInstanceSpec spec,
      NetworkServiceExtensionPosition position,
      final NoErrorCompletion completion) {
    if (!spec.getVmInventory().getType().equals(VmInstanceConstant.USER_VM_TYPE)) {
      completion.done();
      return;
    }

    if (nsExts.isEmpty()) {
      completion.done();
      return;
    }

    // we run into this situation when VM nics are all detached and the
    // VM is being rebooted
    if (spec.getDestNics().isEmpty()) {
      completion.done();
      return;
    }

    List<String> nsTypes = spec.getRequiredNetworkServiceTypes();

    FlowChain schain =
        FlowChainBuilder.newSimpleFlowChain()
            .setName(
                String.format(
                    "release-network-services-from-vm-%s", spec.getVmInventory().getUuid()));
    schain.allowEmptyFlow();

    for (final NetworkServiceExtensionPoint ns : nsExts) {
      if (position != null && ns.getNetworkServiceExtensionPosition() != position) {
        continue;
      }

      if (!nsTypes.contains(ns.getNetworkServiceType().toString())) {
        continue;
      }

      NoRollbackFlow flow =
          new NoRollbackFlow() {
            String __name__ =
                String.format("release-network-service-%s", ns.getNetworkServiceType());

            @Override
            public void run(final FlowTrigger chain, Map data) {
              logger.debug(
                  String.format(
                      "NetworkServiceExtensionPoint[%s] is asking back ends to release network service[%s] if needed",
                      ns.getClass().getName(), ns.getNetworkServiceType()));
              ns.releaseNetworkService(
                  spec,
                  data,
                  new NoErrorCompletion() {
                    @Override
                    public void done() {
                      chain.next();
                    }
                  });
            }
          };

      schain.then(flow);
    }

    schain
        .done(
            new FlowDoneHandler(completion) {
              @Override
              public void handle(Map data) {
                logger.debug(
                    String.format(
                        "successfully released network services for vm[uuid:%s,  name:%s]",
                        spec.getVmInventory().getUuid(), spec.getVmInventory().getName()));
                completion.done();
              }
            })
        .start();
  }