content: // Licensed to the Apache Software Foundation (ASF) under one
  // or more contributor license agreements.  See the NOTICE file
  // distributed with this work for additional information
  // regarding copyright ownership.  The ASF licenses this file
  // to you 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.cloud.vm;
  
  import java.util.concurrent.Executors;
  import java.util.concurrent.ScheduledExecutorService;
  import java.util.concurrent.TimeUnit;
  
  import org.apache.logging.log4j.Logger;
  import org.apache.logging.log4j.LogManager;
  import org.apache.cloudstack.context.CallContext;
  import org.apache.cloudstack.framework.jobs.AsyncJobExecutionContext;
  import org.apache.cloudstack.managed.context.ManagedContextRunnable;
  
  import com.cloud.utils.Pair;
  import com.cloud.utils.concurrency.NamedThreadFactory;
  import com.cloud.utils.db.GlobalLock;
  
  //
  // TODO: simple load scanner, to minimize code changes required in console proxy manager and SSVM, we still leave most of work at handler
  //
  public class SystemVmLoadScanner<T> {
      public enum AfterScanAction {
          nop, expand, shrink
      }
  
      protected Logger logger = LogManager.getLogger(getClass());
  
      private static final int ACQUIRE_GLOBAL_LOCK_TIMEOUT_FOR_COOPERATION = 3;   // 3 seconds
  
      private final SystemVmLoadScanHandler<T> _scanHandler;
      private final ScheduledExecutorService _capacityScanScheduler;
      private final GlobalLock _capacityScanLock;
  
      public SystemVmLoadScanner(SystemVmLoadScanHandler<T> scanHandler) {
          _scanHandler = scanHandler;
          _capacityScanScheduler = Executors.newScheduledThreadPool(1, new NamedThreadFactory(scanHandler.getScanHandlerName()));
          _capacityScanLock = GlobalLock.getInternLock(scanHandler.getScanHandlerName() + ".scan.lock");
      }
  
      public void initScan(long startupDelayMs, long scanIntervalMs) {
          _capacityScanScheduler.scheduleAtFixedRate(getCapacityScanTask(), startupDelayMs, scanIntervalMs, TimeUnit.MILLISECONDS);
      }
  
      public void stop() {
          _capacityScanScheduler.shutdownNow();
  
          try {
              _capacityScanScheduler.awaitTermination(1000, TimeUnit.MILLISECONDS);
          } catch (InterruptedException e) {
              logger.debug("[ignored] interrupted while stopping systemvm load scanner.");
          }
  
          _capacityScanLock.releaseRef();
      }
  
      private Runnable getCapacityScanTask() {
          return new ManagedContextRunnable() {
  
              @Override
              protected void runInContext() {
                  try {
                      CallContext callContext = CallContext.current();
                      assert (callContext != null);
  
                      AsyncJobExecutionContext.registerPseudoExecutionContext(
                          callContext.getCallingAccountId(), callContext.getCallingUserId());
  
                      reallyRun();
  
                      AsyncJobExecutionContext.unregister();
                  } catch (Throwable e) {
                      logger.warn("Unexpected exception " + e.getMessage(), e);
                  }
              }
  
              private void reallyRun() {
                  loadScan();
              }
          };
      }
  
      private void loadScan() {
          if (!_scanHandler.canScan()) {
              return;
          }
  
          if (!_capacityScanLock.lock(ACQUIRE_GLOBAL_LOCK_TIMEOUT_FOR_COOPERATION)) {
              if (logger.isTraceEnabled()) {
                  logger.trace("Capacity scan lock is used by others, skip and wait for my turn");
              }
              return;
          }
  
          try {
              _scanHandler.onScanStart();
  
              T[] pools = _scanHandler.getScannablePools();
              for (T p : pools) {
                  if (_scanHandler.isPoolReadyForScan(p)) {
                      Pair<AfterScanAction, Object> actionInfo = _scanHandler.scanPool(p);
  
                      switch (actionInfo.first()) {
                          case nop:
                              break;
  
                          case expand:
                              _scanHandler.expandPool(p, actionInfo.second());
                              break;
  
                          case shrink:
                              _scanHandler.shrinkPool(p, actionInfo.second());
                              break;
                      }
                  }
              }
  
              _scanHandler.onScanEnd();
  
          } finally {
              _capacityScanLock.unlock();
          }
      }
  }
  