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 org.apache.cloudstack.cluster;
  
  import com.cloud.dc.ClusterVO;
  import com.cloud.host.Host;
  import com.cloud.service.ServiceOfferingVO;
  import com.cloud.utils.Ternary;
  import com.cloud.vm.VirtualMachine;
  import org.apache.cloudstack.framework.config.ConfigKey;
  import org.junit.After;
  import org.junit.Before;
  import org.junit.Test;
  import org.junit.runner.RunWith;
  import org.mockito.InjectMocks;
  import org.mockito.Mockito;
  import org.mockito.MockitoAnnotations;
  import org.mockito.junit.MockitoJUnitRunner;
  
  import javax.naming.ConfigurationException;
  import java.lang.reflect.Field;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  import static org.apache.cloudstack.cluster.ClusterDrsService.ClusterDrsImbalanceThreshold;
  import static org.apache.cloudstack.cluster.ClusterDrsService.ClusterDrsMetric;
  import static org.junit.Assert.assertEquals;
  import static org.junit.Assert.assertFalse;
  import static org.junit.Assert.assertThrows;
  import static org.junit.Assert.assertTrue;
  
  @RunWith(MockitoJUnitRunner.class)
  public class BalancedTest {
  
      @InjectMocks
      Balanced balanced;
  
      VirtualMachine vm1, vm2, vm3;
  
      Host destHost;
  
      ServiceOfferingVO serviceOffering;
  
      ClusterVO cluster;
  
      long clusterId = 1L;
  
      Map<Long, List<VirtualMachine>> hostVmMap;
  
      Map<Long, Ternary<Long, Long, Long>> hostCpuFreeMap, hostMemoryFreeMap;
  
      private AutoCloseable closeable;
  
      @Before
      public void setUp() throws NoSuchFieldException, IllegalAccessException {
          closeable = MockitoAnnotations.openMocks(this);
  
          cluster = Mockito.mock(ClusterVO.class);
  
          vm1 = Mockito.mock(VirtualMachine.class);
          vm2 = Mockito.mock(VirtualMachine.class);
          vm3 = Mockito.mock(VirtualMachine.class); // vm to migrate
  
          destHost = Mockito.mock(Host.class);
          hostVmMap = new HashMap<>();
          hostVmMap.put(1L, Collections.singletonList(vm1));
          hostVmMap.put(2L, Arrays.asList(vm2, vm3));
  
          serviceOffering = Mockito.mock(ServiceOfferingVO.class);
  
          Mockito.when(cluster.getId()).thenReturn(clusterId);
          Mockito.when(vm3.getHostId()).thenReturn(2L);
          Mockito.when(destHost.getId()).thenReturn(1L);
          Mockito.when(serviceOffering.getCpu()).thenReturn(1);
          Mockito.when(serviceOffering.getSpeed()).thenReturn(1000);
          Mockito.when(serviceOffering.getRamSize()).thenReturn(1024);
  
          overrideDefaultConfigValue(ClusterDrsImbalanceThreshold, "_defaultValue", "0.5");
  
          hostCpuFreeMap = new HashMap<>();
          hostCpuFreeMap.put(1L, new Ternary<>(1000L, 0L, 10000L));
          hostCpuFreeMap.put(2L, new Ternary<>(2000L, 0L, 10000L));
  
          hostMemoryFreeMap = new HashMap<>();
          hostMemoryFreeMap.put(1L, new Ternary<>(512L * 1024L * 1024L, 0L, 8192L * 1024L * 1024L));
          hostMemoryFreeMap.put(2L, new Ternary<>(2048L * 1024L * 1024L, 0L, 8192L * 1024L * 1024L));
      }
  
      private void overrideDefaultConfigValue(final ConfigKey configKey, final String name,
              final Object o) throws IllegalAccessException, NoSuchFieldException {
          Field f = ConfigKey.class.getDeclaredField(name);
          f.setAccessible(true);
          f.set(configKey, o);
      }
  
      @After
      public void tearDown() throws Exception {
          closeable.close();
      }
  
      /**
       * <b>needsDrs tests</b>
       * <p>Scenarios to test for needsDrs
       * <p>1. cluster with cpu metric
       * <p>2. cluster with memory metric
       * <p>3. cluster with "unknown" metric
       * <p>
       * <p>CPU imbalance = 0.333
       * <p>Memory imbalance = 0.6
       */
  
      /*
       1. cluster with cpu metric
       0.3333 > 0.5 -> False
      */
      @Test
      public void needsDrsWithCpu() throws ConfigurationException, NoSuchFieldException, IllegalAccessException {
          overrideDefaultConfigValue(ClusterDrsMetric, "_defaultValue", "cpu");
          assertFalse(balanced.needsDrs(cluster, new ArrayList<>(hostCpuFreeMap.values()), new ArrayList<>(hostMemoryFreeMap.values())));
      }
  
      /*
       2. cluster with memory metric
       0.6 > 0.5 -> True
      */
      @Test
      public void needsDrsWithMemory() throws ConfigurationException, NoSuchFieldException, IllegalAccessException {
          overrideDefaultConfigValue(ClusterDrsMetric, "_defaultValue", "memory");
          assertTrue(balanced.needsDrs(cluster, new ArrayList<>(hostCpuFreeMap.values()), new ArrayList<>(hostMemoryFreeMap.values())));
      }
  
      /* 3. cluster with "unknown" metric */
      @Test
      public void needsDrsWithUnknown() throws NoSuchFieldException, IllegalAccessException {
          overrideDefaultConfigValue(ClusterDrsMetric, "_defaultValue", "unknown");
          assertThrows(ConfigurationException.class, () -> balanced.needsDrs(cluster, new ArrayList<>(hostCpuFreeMap.values()), new ArrayList<>(hostMemoryFreeMap.values())));
      }
  
      /**
       * getMetrics tests
       * <p>Scenarios to test for getMetrics
       * <p>1. cluster with cpu metric
       * <p>2. cluster with memory metric
       * <p>3. cluster with default metric
       * <p>
       * <p>Pre
       * <p>CPU imbalance = 0.333333
       * <p>Memory imbalance = 0.6
       * <p>
       * <p>Post
       * <p>CPU imbalance = 0.3333
       * <p>Memory imbalance = 0.2
       * <p>
       * <p>Cost 512.0
       * <p>Benefit  (0.6-0.2) * 8192 = 3276.8
       */
  
      /*
       1. cluster with cpu metric
       improvement = 0.3333 - 0.3333  = 0.0
      */
      @Test
      public void getMetricsWithCpu() throws NoSuchFieldException, IllegalAccessException, ConfigurationException {
          overrideDefaultConfigValue(ClusterDrsMetric, "_defaultValue", "cpu");
          Ternary<Double, Double, Double> result = balanced.getMetrics(cluster, vm3, serviceOffering, destHost,
                  hostCpuFreeMap, hostMemoryFreeMap, false);
          assertEquals(0.0, result.first(), 0.01);
          assertEquals(0.0, result.second(), 0.0);
          assertEquals(1.0, result.third(), 0.0);
      }
  
      /*
       2. cluster with memory metric
       improvement = 0.6 - 0.2 = 0.4
      */
      @Test
      public void getMetricsWithMemory() throws NoSuchFieldException, IllegalAccessException, ConfigurationException {
          overrideDefaultConfigValue(ClusterDrsMetric, "_defaultValue", "memory");
          Ternary<Double, Double, Double> result = balanced.getMetrics(cluster, vm3, serviceOffering, destHost,
                  hostCpuFreeMap, hostMemoryFreeMap, false);
          assertEquals(0.4, result.first(), 0.01);
          assertEquals(0, result.second(), 0.0);
          assertEquals(1, result.third(), 0.0);
      }
  }
  