content: 
  /*
   * Copyright Contributors to the OpenCue Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
   * in compliance with the License. You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software distributed under the License
   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
   * or implied. See the License for the specific language governing permissions and limitations under
   * the License.
   */
  
  package com.imageworks.spcue;
  
  import com.imageworks.spcue.dispatcher.Dispatcher;
  import com.imageworks.spcue.grpc.host.ThreadMode;
  
  import org.apache.logging.log4j.Logger;
  import org.apache.logging.log4j.LogManager;
  
  public class VirtualProc extends FrameEntity implements ProcInterface {
  
      private static final Logger logger = LogManager.getLogger(VirtualProc.class);
  
      public String hostId;
      public String allocationId;
      public String frameId;
      public String hostName;
      public String os;
      public byte[] childProcesses;
  
      public boolean canHandleNegativeCoresRequest;
      public int coresReserved;
      public long memoryReserved;
      public long memoryUsed;
      public long memoryMax;
      public long virtualMemoryUsed;
      public long virtualMemoryMax;
  
      public int gpusReserved;
      public long gpuMemoryReserved;
      public long gpuMemoryUsed;
      public long gpuMemoryMax;
  
      public boolean unbooked;
      public boolean usageRecorded = false;
      public boolean isLocalDispatch = false;
  
      public String getProcId() {
          return id;
      }
  
      public String getHostId() {
          return hostId;
      }
  
      public String getAllocationId() {
          return allocationId;
      }
  
      public String getFrameId() {
          return frameId;
      }
  
      public String getName() {
          return hostName;
      }
  
      /**
       * Build and return a proc in either fast or efficient mode.
       *
       * Efficient mode tries to assign one core per frame, but may upgrade the number of cores based
       * on memory usage.
       *
       * Fast mode books all the idle cores on the the host at one time.
       *
       * @param host
       * @param frame
       * @return
       */
      public static final VirtualProc build(DispatchHost host, DispatchFrame frame,
              String... selfishServices) {
          VirtualProc proc = new VirtualProc();
          proc.allocationId = host.getAllocationId();
          proc.hostId = host.getHostId();
          proc.frameId = null;
          proc.layerId = frame.getLayerId();
          proc.jobId = frame.getJobId();
          proc.showId = frame.getShowId();
          proc.facilityId = frame.getFacilityId();
          proc.os = frame.os;
  
          proc.hostName = host.getName();
          proc.unbooked = false;
          proc.isLocalDispatch = host.isLocalDispatch;
  
          proc.coresReserved = frame.minCores;
          proc.memoryReserved = frame.getMinMemory();
          proc.gpusReserved = frame.minGpus;
          proc.gpuMemoryReserved = frame.minGpuMemory;
  
          /*
           * Frames that are announcing cores less than 100 are not multi-threaded so there is no
           * reason for the frame to span more than a single core.
           *
           * If we are in "fast mode", we just book all the cores If the host is nimby, desktops are
           * automatically fast mode.
           */
  
          if (host.strandedCores > 0) {
              proc.coresReserved = proc.coresReserved + host.strandedCores;
          }
  
          proc.canHandleNegativeCoresRequest = host.canHandleNegativeCoresRequest(proc.coresReserved);
  
          if (proc.coresReserved == 0) {
              logger.debug("Reserving all cores");
              proc.coresReserved = host.cores;
          } else if (proc.coresReserved < 0) {
              logger.debug("Reserving all cores minus " + proc.coresReserved);
              proc.coresReserved = host.cores + proc.coresReserved;
          } else if (proc.coresReserved >= 100) {
  
              int originalCores = proc.coresReserved;
  
              /*
               * wholeCores could be 0 if we have a fraction of a core, we can just throw a re
               */
              int wholeCores = (int) (Math.floor(host.idleCores / 100.0));
              if (wholeCores == 0) {
                  throw new EntityException("The host had only a fraction of a core remaining "
                          + "but the frame required " + frame.minCores);
              }
  
              // if (host.threadMode == ThreadMode.Variable.value() &&
              // CueUtil.isDayTime()) {
              if (host.threadMode == ThreadMode.ALL_VALUE) {
                  proc.coresReserved = wholeCores * 100;
              } else {
                  if (frame.threadable) {
                      if (selfishServices != null && frame.services != null
                              && containsSelfishService(frame.services.split(","), selfishServices)) {
                          proc.coresReserved = wholeCores * 100;
                      } else {
                          if (host.idleMemory
                                  - frame.getMinMemory() <= Dispatcher.MEM_STRANDED_THRESHHOLD) {
                              proc.coresReserved = wholeCores * 100;
                          } else {
                              proc.coresReserved = getCoreSpan(host, frame.getMinMemory());
                          }
                      }
                      if (host.threadMode == ThreadMode.VARIABLE_VALUE && proc.coresReserved <= 200) {
                          proc.coresReserved = 200;
                          if (proc.coresReserved > host.idleCores) {
                              // Do not allow threadable frame running on 1 core.
                              throw new JobDispatchException(
                                      "Do not allow threadable frame running one core on a ThreadMode.Variable host.");
                          }
                      }
                  }
              }
  
              /*
               * Sanity checks to ensure coreUnits are not to high or to low.
               */
              if (proc.coresReserved < 100) {
                  proc.coresReserved = 100;
              }
  
              /*
               * If the core value is changed it can never fall below the original.
               */
              if (proc.coresReserved < originalCores) {
                  proc.coresReserved = originalCores;
              }
  
              /*
               * Check to ensure we haven't exceeded max cores.
               */
              if (frame.maxCores > 0 && proc.coresReserved >= frame.maxCores) {
                  proc.coresReserved = frame.maxCores;
              }
  
              if (proc.coresReserved > host.idleCores) {
                  if (host.threadMode == ThreadMode.VARIABLE_VALUE && frame.threadable
                          && wholeCores == 1) {
                      throw new JobDispatchException(
                              "Do not allow threadable frame running one core on a ThreadMode.Variable host.");
                  }
                  proc.coresReserved = wholeCores * 100;
              }
          }
  
          /*
           * Don't thread non-threadable layers, no matter what people put for the number of cores.
           */
          if (!frame.threadable && proc.coresReserved > 100) {
              proc.coresReserved = 100;
          }
  
          return proc;
      }
  
      private static final boolean containsSelfishService(String[] frameServices,
              String[] selfishServices) {
          for (String frameService : frameServices) {
              for (String selfishService : selfishServices) {
                  if (frameService.equals(selfishService)) {
                      return true;
                  }
              }
          }
          return false;
      }
  
      public static final VirtualProc build(DispatchHost host, DispatchFrame frame,
              LocalHostAssignment lja) {
  
          VirtualProc proc = new VirtualProc();
          proc.allocationId = host.getAllocationId();
          proc.hostId = host.getHostId();
          proc.frameId = null;
          proc.layerId = frame.getLayerId();
          proc.jobId = frame.getJobId();
          proc.showId = frame.getShowId();
          proc.facilityId = frame.getFacilityId();
          proc.os = frame.os;
  
          proc.hostName = host.getName();
          proc.unbooked = false;
          proc.isLocalDispatch = host.isLocalDispatch;
  
          proc.coresReserved = lja.getThreads() * 100;
          proc.memoryReserved = frame.getMinMemory();
          proc.gpusReserved = frame.minGpus;
          proc.gpuMemoryReserved = frame.minGpuMemory;
  
          int wholeCores = (int) (Math.floor(host.idleCores / 100.0));
          if (wholeCores == 0) {
              throw new EntityException("The host had only a fraction of a core remaining "
                      + "but the frame required " + frame.minCores);
          }
  
          if (proc.coresReserved > host.idleCores) {
              proc.coresReserved = wholeCores * 100;
          }
  
          return proc;
  
      }
  
      /**
       * Allocates additional cores when the frame is using more 50% more than a single cores worth of
       * memory.
       *
       * @param host
       * @param minMemory
       * @return
       */
      public static int getCoreSpan(DispatchHost host, long minMemory) {
          int totalCores = (int) (Math.floor(host.cores / 100.0));
          int idleCores = (int) (Math.floor(host.idleCores / 100.0));
          if (idleCores < 1) {
              return 100;
          }
  
          long memPerCore = host.idleMemory / totalCores;
          double procs = minMemory / (double) memPerCore;
          int reserveCores = (int) (Math.round(procs)) * 100;
  
          return reserveCores;
      }
  }
  