/** Choose which datapoint to use. */
  public enum TimeScale {
    SEC10(TimeUnit2.SECONDS.toMillis(10)),
    MIN(TimeUnit2.MINUTES.toMillis(1)),
    HOUR(TimeUnit2.HOURS.toMillis(1));

    /** Number of milliseconds (10 secs, 1 min, and 1 hour) that this constant represents. */
    public final long tick;

    TimeScale(long tick) {
      this.tick = tick;
    }

    /** Creates a new {@link DateFormat} suitable for processing this {@link TimeScale}. */
    public DateFormat createDateFormat() {
      switch (this) {
        case HOUR:
          return new SimpleDateFormat("MMM/dd HH");
        case MIN:
          return new SimpleDateFormat("HH:mm");
        case SEC10:
          return new SimpleDateFormat("HH:mm:ss");
        default:
          throw new AssertionError();
      }
    }

    /** Parses the {@link TimeScale} from the query parameter. */
    public static TimeScale parse(String type) {
      if (type == null) return TimeScale.MIN;
      return Enum.valueOf(TimeScale.class, type.toUpperCase(Locale.ENGLISH));
    }
  }
 /**
  * Turns an interval into milliseconds./
  *
  * @param interval the interval.
  * @return the milliseconds.
  */
 private static long toIntervalMillis(String interval) {
   TimeUnit2 units = TimeUnit2.MINUTES;
   interval = interval.toLowerCase();
   if (interval.endsWith("h")) {
     units = TimeUnit2.HOURS;
     interval = StringUtils.removeEnd(interval, "h");
   }
   if (interval.endsWith("m")) {
     interval = StringUtils.removeEnd(interval, "m");
   } else if (interval.endsWith("d")) {
     units = TimeUnit2.DAYS;
     interval = StringUtils.removeEnd(interval, "d");
   } else if (interval.endsWith("ms")) {
     units = TimeUnit2.SECONDS;
     interval = StringUtils.removeEnd(interval, "ms");
   } else if (interval.endsWith("s")) {
     units = TimeUnit2.SECONDS;
     interval = StringUtils.removeEnd(interval, "s");
   }
   long value = 0;
   try {
     value = Long.parseLong(interval);
   } catch (NumberFormatException e) {
     value = 1;
   }
   return Math.min(
       TimeUnit2.DAYS.toMillis(30),
       Math.max(TimeUnit2.MINUTES.toMillis(1), units.toMillis(value)));
 }
 /**
  * Turns an interval into a suitable crontab.
  *
  * @param interval the interval.
  * @return the crontab.
  */
 private static String toCrontab(String interval) {
   long millis = toIntervalMillis(interval);
   if (millis < TimeUnit2.MINUTES.toMillis(5)) {
     return "* * * * *";
   }
   if (millis < TimeUnit2.MINUTES.toMillis(10)) {
     return "*/12 * * * *";
   }
   if (millis < TimeUnit2.MINUTES.toMillis(30)) {
     return "*/6 * * * *";
   }
   if (millis < TimeUnit2.HOURS.toMillis(1)) {
     return "*/2 * * * *";
   }
   if (millis < TimeUnit2.HOURS.toMillis(8)) {
     return "H * * * *";
   }
   return "H H * * *";
 }
 @Override
 public synchronized long check(VMLabMgrComputer computer) {
   if (computer.isIdle()) {
     final long idleTime = System.currentTimeMillis() - computer.getIdleStartMilliseconds();
     if (idleTime > TimeUnit2.MINUTES.toMillis(1)) {
       LOGGER.info(MessageFormat.format("Disconnecting from {0}", computer.getName()));
       computer.getNode().terminate();
     }
   }
   return 1l;
 }
/**
 * Default convenience implementation of {@link RetentionStrategy} for slaves provisioned from
 * {@link Cloud}.
 *
 * <p>If a slave is idle for 10 mins, this retention strategy will remove the slave. This can be
 * used as-is for a {@link Node} provisioned by cloud to implement the auto-scaling semantics, it
 * can be subtyped to tweak the behavior, or it can be used as an example.
 *
 * <p>TODO {@link CloudRetentionStrategy} seems to be a better implementation.
 *
 * @author Kohsuke Kawaguchi
 * @since 1.510
 */
public class CloudSlaveRetentionStrategy<T extends Computer> extends RetentionStrategy<T> {

  @Override
  @GuardedBy("hudson.model.Queue.lock")
  public long check(T c) {
    if (!c.isConnecting() && c.isAcceptingTasks()) {
      if (isIdleForTooLong(c)) {
        try {
          Node n = c.getNode();
          if (n
              != null) // rare, but n==null if the node is deleted and being checked roughly at the
            // same time
            kill(n);
        } catch (IOException e) {
          LOGGER.log(Level.WARNING, "Failed to remove " + c.getDisplayName(), e);
        }
      }
    }
    return checkCycle();
  }

  /**
   * Remove the node.
   *
   * <p>To actually deallocate the resource tied to this {@link Node}, implement {@link
   * Computer#onRemoved()}.
   */
  protected void kill(Node n) throws IOException {
    Jenkins.getInstance().removeNode(n);
  }

  /** When do we check again next time? */
  protected long checkCycle() {
    return getIdleMaxTime() / 10;
  }

  /** Has this computer been idle for too long? */
  protected boolean isIdleForTooLong(T c) {
    return System.currentTimeMillis() - c.getIdleStartMilliseconds() > getIdleMaxTime();
  }

  /** If the computer has been idle longer than this time, we'll kill the slave. */
  protected long getIdleMaxTime() {
    return TIMEOUT;
  }

  // for debugging, it's convenient to be able to reduce this time
  public static long TIMEOUT =
      Long.getLong(
          CloudSlaveRetentionStrategy.class.getName() + ".timeout", TimeUnit2.MINUTES.toMillis(10));

  private static final Logger LOGGER =
      Logger.getLogger(CloudSlaveRetentionStrategy.class.getName());
}
  private Connection connectToSsh(Computer computer, PrintStream logger)
      throws RequestUnsuccessfulException, DigitalOceanException {

    final long timeout = TimeUnit2.MINUTES.toMillis(computer.getCloud().getTimeoutMinutes());
    final long startTime = System.currentTimeMillis();
    final int sleepTime = 10;

    long waitTime;

    while ((waitTime = System.currentTimeMillis() - startTime) < timeout) {

      // Hack to fetch this each time through the loop to get the latest information.
      final Droplet droplet =
          DigitalOcean.getDroplet(
              computer.getCloud().getAuthToken(), computer.getNode().getDropletId());

      if (isDropletStarting(droplet)) {
        logger.println(
            "Waiting for droplet to enter ACTIVE state. Sleeping " + sleepTime + " seconds.");
      } else {
        try {
          final String host = getIpAddress(computer);

          if (Strings.isNullOrEmpty(host) || "0.0.0.0".equals(host)) {
            logger.println(
                "No ip address yet, your host is most likely waiting for an ip address.");
          } else {
            int port = computer.getSshPort();

            Connection conn = getDropletConnection(host, port, logger);
            if (conn != null) {
              return conn;
            }
          }
        } catch (IOException e) {
          // Ignore, we'll retry.
        }
        logger.println("Waiting for SSH to come up. Sleeping " + sleepTime + " seconds.");
      }

      sleep(sleepTime);
    }

    throw new RuntimeException(
        format(
            "Timed out after %d seconds of waiting for ssh to become available (max timeout configured is %s)",
            waitTime / 1000, timeout / 1000));
  }
 /**
  * Returns the interval between indexing.
  *
  * @return the interval between indexing.
  */
 @SuppressWarnings("unused") // used by Jelly EL
 public String getInterval() {
   if (interval < TimeUnit2.SECONDS.toMillis(1)) {
     return Long.toString(interval) + "ms";
   }
   if (interval < TimeUnit2.MINUTES.toMillis(1)) {
     return Long.toString(TimeUnit2.MILLISECONDS.toSeconds(interval)) + "s";
   }
   if (interval < TimeUnit2.HOURS.toMillis(1)) {
     return Long.toString(TimeUnit2.MILLISECONDS.toMinutes(interval)) + "m";
   }
   if (interval < TimeUnit2.DAYS.toMillis(1)) {
     return Long.toString(TimeUnit2.MILLISECONDS.toHours(interval)) + "h";
   }
   return Long.toString(TimeUnit2.MILLISECONDS.toDays(interval)) + "d";
 }
 @Override
 public synchronized long check(JCloudsComputer c) {
   if (c.isIdle() && !c.getNode().isPendingDelete() && !disabled) {
     // Get the retention time, in minutes, from the JCloudsCloud this JCloudsComputer belongs to.
     final int retentionTime = JCloudsCloud.getByName(c.getCloudName()).getRetentionTime();
     if (retentionTime > -1) {
       final long idleMilliseconds = System.currentTimeMillis() - c.getIdleStartMilliseconds();
       if (idleMilliseconds > TimeUnit2.MINUTES.toMillis(retentionTime)) {
         LOGGER.info("Setting " + c.getName() + " to be deleted.");
         if (!c.isOffline()) {
           c.setTemporarilyOffline(true, OfflineCause.create(Messages._DeletedCause()));
         }
         c.getNode().setPendingDelete(true);
       }
     }
   }
   return 1;
 }