private void applyNetworkServices(
      final VmInstanceSpec spec,
      NetworkServiceExtensionPosition position,
      final Completion completion) {
    if (!spec.getVmInventory().getType().equals(VmInstanceConstant.USER_VM_TYPE)) {
      completion.success();
      return;
    }

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

    if (spec.getL3Networks() == null || spec.getL3Networks().isEmpty()) {
      completion.success();
      return;
    }

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

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

    FlowChain schain =
        FlowChainBuilder.newSimpleFlowChain()
            .setName(
                String.format("apply-network-service-to-vm-%s", spec.getVmInventory().getUuid()));
    schain.allowEmptyFlow();
    for (final NetworkServiceExtensionPoint ns : nsExts) {
      if (ns.getNetworkServiceExtensionPosition() != position) {
        continue;
      }

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

      Flow flow =
          new Flow() {
            String __name__ = String.format("apply-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 apply network service[%s] if needed",
                      ns.getClass().getName(), ns.getNetworkServiceType()));
              ns.applyNetworkService(
                  spec,
                  data,
                  new Completion() {
                    @Override
                    public void success() {
                      chain.next();
                    }

                    @Override
                    public void fail(ErrorCode errorCode) {
                      chain.fail(errorCode);
                    }
                  });
            }

            @Override
            public void rollback(final FlowRollback 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.rollback();
                    }
                  });
            }
          };

      schain.then(flow);
    }

    schain
        .error(
            new FlowErrorHandler() {
              @Override
              public void handle(ErrorCode err, Map data) {
                completion.fail(err);
              }
            })
        .done(
            new FlowDoneHandler() {
              @Override
              public void handle(Map data) {
                completion.success();
              }
            })
        .start();
  }