@Override
  public boolean insertActions(Solution s, ReconfigurationPlan plan) {
    DurationEvaluators dev = rp.getDurationEvaluators();
    // Only if the VM doesn't stay
    if (s.getIntVal(cSlice.getHoster()) != (s.getIntVal(dSlice.getHoster()))) {
      Action a;
      Node dst = rp.getNode(s.getIntVal(dSlice.getHoster()));
      // Migration
      if (s.getIntVal(doReinstantiation) == 0) {
        int st = s.getIntVal(getStart());
        int ed = s.getIntVal(getEnd());

        if (getBandwidth() != null) {
          a = new MigrateVM(vm, src, dst, st, ed, s.getIntVal(getBandwidth()));
        } else {
          a = new MigrateVM(vm, src, dst, st, ed);
        }
        plan.add(a);
        // Re-instantiation
      } else {
        VM newVM = rp.cloneVM(vm);
        if (newVM == null) {
          rp.getLogger().error("Unable to get a new int to plan the re-instantiate of VM {}", vm);
          return false;
        }
        org.btrplace.plan.event.ForgeVM fvm =
            new org.btrplace.plan.event.ForgeVM(
                newVM,
                s.getIntVal(dSlice.getStart())
                    - dev.evaluate(rp.getSourceModel(), org.btrplace.plan.event.ForgeVM.class, vm),
                s.getIntVal(dSlice.getStart()));
        // forge the new VM from a template
        plan.add(fvm);
        // Boot the new VM
        int endForging = fvm.getEnd();
        org.btrplace.plan.event.BootVM boot =
            new org.btrplace.plan.event.BootVM(
                newVM,
                dst,
                endForging,
                endForging
                    + dev.evaluate(
                        rp.getSourceModel(), org.btrplace.plan.event.BootVM.class, newVM));
        boot.addEvent(Action.Hook.PRE, new SubstitutedVMEvent(vm, newVM));
        return plan.add(boot)
            && plan.add(
                new org.btrplace.plan.event.ShutdownVM(
                    vm, src, boot.getEnd(), s.getIntVal(cSlice.getEnd())));
      }
    }
    return true;
  }
  /**
   * Make a new model.
   *
   * @param p the RP to use as a basis.
   * @param e the VM managed by the action
   * @throws org.btrplace.scheduler.SchedulerException if an error occurred
   */
  public RelocatableVM(ReconfigurationProblem p, VM e) throws SchedulerException {

    // Get vars
    vm = e;
    rp = p;
    src = rp.getSourceModel().getMapping().getVMLocation(e);
    org.chocosolver.solver.Model csp = rp.getModel();
    Model mo = rp.getSourceModel();

    // Default values
    start = rp.getStart();
    end = rp.getStart();
    duration = csp.intVar(0);
    state = csp.boolVar(true);

    // If not manageable, the VM stays on the current host
    if (!p.getManageableVMs().contains(e)) {
      stay = csp.boolVar(true);
      doReinstantiation = csp.boolVar(false);
      manageable = false;

      IntVar host = rp.makeCurrentHost(vm, PREFIX_STAY, vm, ").host");
      cSlice =
          new SliceBuilder(rp, vm, PREFIX_STAY, vm.toString(), ").cSlice")
              .setHoster(host)
              .setEnd(rp.makeUnboundedDuration(PREFIX_STAY, vm, ").cSlice_end"))
              .build();
      dSlice =
          new SliceBuilder(rp, vm, PREFIX_STAY, vm, ").dSlice")
              .setHoster(host)
              .setStart(cSlice.getEnd())
              .build();

      return;
    }

    // The VM can move (to re-instantiate or migrate) OR STAY to the same host
    stay = csp.boolVar(rp.makeVarLabel(vm, "stay"));
    cSlice =
        new SliceBuilder(rp, vm, PREFIX, vm, ").cSlice")
            .setHoster(rp.getNode(rp.getSourceModel().getMapping().getVMLocation(vm)))
            .setEnd(rp.makeUnboundedDuration(PREFIX, vm, ").cSlice_end"))
            .build();

    dSlice =
        new SliceBuilder(rp, vm, PREFIX, vm, ").dSlice")
            .setStart(rp.makeUnboundedDuration(PREFIX, vm, ").dSlice_start"))
            .build();

    // Update start and end vars of the action
    start = dSlice.getStart();
    end = cSlice.getEnd();

    csp.post(new Arithmetic(end, Operator.LE, rp.getEnd()));
    // Get some static durations from evaluators
    DurationEvaluators dev = rp.getDurationEvaluators();
    int migrateDuration = dev.evaluate(rp.getSourceModel(), MigrateVM.class, vm);
    int bootDuration = dev.evaluate(rp.getSourceModel(), org.btrplace.plan.event.BootVM.class, vm);
    int forgeD =
        p.getDurationEvaluators()
            .evaluate(p.getSourceModel(), org.btrplace.plan.event.ForgeVM.class, vm);

    // Compute the re-instantiation duration
    int reInstantiateDuration = bootDuration + forgeD;
    reInstantiateDuration = forgeD; // Compliant with CMaxOnlineTest and others

    // Get the networking view if attached
    Network network = Network.get(mo);
    IntVar migrationDuration;
    if (network != null) {

      // Set the migration algorithm
      postCopy = mo.getAttributes().get(vm, "postCopy", false);

      // Create unbounded/large domain vars for migration duration and bandwidth
      migrationDuration = p.makeUnboundedDuration("migration(", vm, ").duration");
      bandwidth = csp.intVar(PREFIX + vm + ").bandwidth", 0, Integer.MAX_VALUE / 100, true);
    }
    // No networking view, set the duration from the evaluator
    else {
      // The duration can still be 0 => the VM STAY !
      migrationDuration =
          csp.intVar(
              rp.makeVarLabel("migration(", vm, ").duration"), new int[] {0, migrateDuration});
      bandwidth = null;
    }

    // Possibly re-instantiate (if some attributes are defined)
    if (mo.getAttributes().get(vm, "clone", false) && mo.getAttributes().isSet(vm, "template")) {

      doReinstantiation = csp.boolVar(rp.makeVarLabel("relocation_method(", vm, ")"));

      duration =
          csp.intVar(
              rp.makeVarLabel(PREFIX, vm, ").duration"),
              Math.min(migrationDuration.getLB(), reInstantiateDuration),
              Math.max(migrationDuration.getUB(), reInstantiateDuration),
              true);

      // Re-instantiate or migrate
      // (Prefer the re-instantiation if the duration are the same, otherwise choose the min)
      rp.getModel()
          .ifThenElse(
              rp.getModel()
                  .or(
                      new Arithmetic(
                          doReinstantiation, Operator.EQ, 0), // can be instantiated externally !
                      new Arithmetic(migrationDuration, Operator.LT, reInstantiateDuration)),
              new Arithmetic(duration, Operator.EQ, migrationDuration),
              new Arithmetic(duration, Operator.EQ, reInstantiateDuration));

      // If it is a re-instantiation then specify that the dSlice must start AFTER the Forge delay
      IntVar time =
          csp.intVar(rp.makeVarLabel(doReinstantiation.getName(), " * ", forgeD), 0, forgeD, false);
      csp.post(csp.times(doReinstantiation, forgeD, time));
      csp.post(new Arithmetic(start, Operator.GE, time));
      // Be sure that doReinstantiation will be instantiated
      csp.post(new FastIFFEq(doReinstantiation, duration, reInstantiateDuration));
    }
    // The VM either migrate or stay but won't be re-instantiated for sure
    else {
      doReinstantiation = csp.boolVar(false);
      duration = migrationDuration;
    }

    // If the VM stay (src host == dst host), then duration = 0
    csp.post(new FastIFFEq(stay, dSlice.getHoster(), cSlice.getHoster().getValue()));
    csp.post(new FastIFFEq(stay, duration, 0));
    // We have to force the migration duration equals to 0 if it stays
    // otherwise, the variable will be free
    csp.post(new FastIFFEq(stay, migrationDuration, 0));

    // Create the task ('default' cumulative constraint with a height of 1)
    migrationTask = new Task(start, duration, end);
  }