/*
* Copyright 2015 Netflix, Inc.
*
* 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.netflix.fenzo;
import com.netflix.fenzo.plugins.BinPackingFitnessCalculators;
import org.junit.Assert;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.netflix.fenzo.functions.Action1;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class BinPackingSchedulerTests {
private static final Logger logger = LoggerFactory.getLogger(BinPackingSchedulerTests.class);
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
}
private TaskScheduler getScheduler(VMTaskFitnessCalculator fitnessCalculator) {
return new TaskScheduler.Builder()
.withFitnessCalculator(fitnessCalculator)
.withLeaseOfferExpirySecs(1000000)
.withLeaseRejectAction(new Action1<VirtualMachineLease>() {
@Override
public void call(VirtualMachineLease virtualMachineLease) {
logger.info("Rejecting lease on " + virtualMachineLease.hostname());
}
})
.build();
}
@Test
public void testCPUBinPacking1() {
double totalCores=4;
double usedCores=1;
double totalMemory=100;
double usedMemory=10;
TaskScheduler scheduler = getScheduler(BinPackingFitnessCalculators.cpuBinPacker);
List<VirtualMachineLease> leases = LeaseProvider.getLeases(2, totalCores, totalMemory, 1, 10);
List<TaskRequest> taskRequests = new ArrayList<>();
taskRequests.add(TaskRequestProvider.getTaskRequest(usedCores, usedMemory, 1));
Map<String,VMAssignmentResult> resultMap = scheduler.scheduleOnce(taskRequests, leases).getResultMap();
Assert.assertEquals(1, resultMap.size());
String usedHostname = resultMap.keySet().iterator().next();
leases.clear();
leases.add(LeaseProvider.getLeaseOffer(usedHostname, totalCores-usedCores, totalMemory-usedMemory, 1, 10));
taskRequests.clear();
taskRequests.add(TaskRequestProvider.getTaskRequest(usedCores, usedMemory, 1));
resultMap = scheduler.scheduleOnce(taskRequests, leases).getResultMap();
Assert.assertEquals(1, resultMap.size());
Assert.assertEquals(usedHostname, resultMap.keySet().iterator().next());
}
@Test
public void testCPUBinPacking2() {
double totalCores=4;
double usedCores=1;
double totalMemory=100;
double usedMemory=10;
TaskRequest task1 = TaskRequestProvider.getTaskRequest(usedCores, usedMemory, 1);
TaskRequest task2 = TaskRequestProvider.getTaskRequest(usedCores*2, usedMemory, 1);
TaskScheduler scheduler = getScheduler(BinPackingFitnessCalculators.cpuBinPacker);
List<VirtualMachineLease> leases = LeaseProvider.getLeases(2, totalCores, totalMemory, 1, 10);
List<TaskRequest> taskRequests = new ArrayList<>();
taskRequests.add(task1);
// First schedule just task1 with both leases, one of the leases should get used
Map<String,VMAssignmentResult> resultMap = scheduler.scheduleOnce(taskRequests, leases).getResultMap();
Assert.assertEquals(1, resultMap.size());
String usedHostname1 = resultMap.keySet().iterator().next();
scheduler.getTaskAssigner().call(task1, usedHostname1);
taskRequests.clear();
taskRequests.add(task2);
leases.clear();
// Now submit task2 without any new leases. The other lease should get used
resultMap = scheduler.scheduleOnce(taskRequests, leases).getResultMap();
Assert.assertEquals(1, resultMap.size());
String usedHostname2 = resultMap.keySet().iterator().next();
Assert.assertTrue(!usedHostname1.equals(usedHostname2));
scheduler.getTaskAssigner().call(task2, usedHostname2);
// Now add back both leases with remaining resources and submit task 3. Should go to the 2nd host's lease since
// more of its CPUs are already in use
leases.add(LeaseProvider.getLeaseOffer(usedHostname2, totalCores-task2.getCPUs(), totalMemory-task2.getMemory(), 2, 10));
leases.add(LeaseProvider.getLeaseOffer(usedHostname1, totalCores-task1.getCPUs(), totalMemory-task1.getMemory(), 2, 10));
taskRequests.clear();
taskRequests.add(TaskRequestProvider.getTaskRequest(usedCores, usedMemory, 1));
resultMap = scheduler.scheduleOnce(taskRequests, leases).getResultMap();
Assert.assertEquals(1, resultMap.size());
Assert.assertTrue("CPU Bin packing failed", usedHostname2.equals(resultMap.keySet().iterator().next()));
}
@Test
public void testCPUBinPackingWithSeveralHosts() {
testBinPackingWithSeveralHosts("CPU");
}
@Test
public void testMemoryBinPackingWithSeveralHosts() {
testBinPackingWithSeveralHosts("Memory");
}
@Test
public void testNetworkBinPackingWithSeveralHosts() {
testBinPackingWithSeveralHosts("Network");
}
private void testBinPackingWithSeveralHosts(String resource) {
TaskScheduler scheduler=null;
switch (resource) {
case "CPU":
scheduler = getScheduler(BinPackingFitnessCalculators.cpuBinPacker);
break;
case "Memory":
scheduler = getScheduler(BinPackingFitnessCalculators.memoryBinPacker);
break;
case "Network":
scheduler = getScheduler(BinPackingFitnessCalculators.networkBinPacker);
break;
default:
Assert.fail("Unknown resource type " + resource);
}
double cpuCores1=4;
double cpuCores2=8;
double memory1=400;
double memory2=800;
double network1=400;
double network2 = 800;
int N = 10; // #instances
// First create N 8-core machines and then N 4-core machines
List<VirtualMachineLease> leases = LeaseProvider.getLeases(N, cpuCores2, memory2, network2, 1, 100);
leases.addAll(LeaseProvider.getLeases(N, N, cpuCores1, memory1, network1, 1, 100));
// Create as many tasks as to fill all of the 4-core machines, and then one more
List<TaskRequest> taskRequests = new ArrayList<>();
for(int i=0; i<N*cpuCores1+1; i++)
taskRequests.add(TaskRequestProvider.getTaskRequest(1, memory1/cpuCores1, network1/cpuCores1, 1));
Map<String,VMAssignmentResult> resultMap = scheduler.scheduleOnce(taskRequests, leases).getResultMap();
Assert.assertEquals(N+1, resultMap.size());
int hosts1=0;
int hosts2=0;
for(VMAssignmentResult result: resultMap.values()) {
List<VirtualMachineLease> leasesUsed = result.getLeasesUsed();
Assert.assertEquals(1, leasesUsed.size());
if(leasesUsed.get(0).cpuCores() == cpuCores1)
hosts1++;
else
hosts2++;
}
Assert.assertEquals(N, hosts1);
Assert.assertEquals(1, hosts2);
}
/**
* Test memory bin packing when servers have memory inversely proportional to CPUs
* @throws Exception
*/
@Test
public void testMemoryBinPacking2() throws Exception {
double cpus1=4;
double memory1=800;
double cpus2=8;
double memory2=400;
int N=10;
// First create N servers of cpus1/memory1 and then N of cpus2/memory2
List<VirtualMachineLease> leases = LeaseProvider.getLeases(N, cpus1, memory1, 1, 100);
leases.addAll(LeaseProvider.getLeases(N, N, cpus2, memory2, 1, 100));
// create as many tasks as to fill all of the memory2 hosts, and then one more
List<TaskRequest> taskRequests = new ArrayList<>();
double memToAsk=100;
for(int i=0; i<(N*memory2/memToAsk)+1; i++)
taskRequests.add(TaskRequestProvider.getTaskRequest(1, memToAsk, 1));
TaskScheduler scheduler = getScheduler(BinPackingFitnessCalculators.memoryBinPacker);
SchedulingResult schedulingResult = scheduler.scheduleOnce(taskRequests, leases);
Assert.assertEquals(N+1, schedulingResult.getResultMap().size());
int hosts1=0;
int hosts2=0;
for(VMAssignmentResult result: schedulingResult.getResultMap().values()) {
List<VirtualMachineLease> leasesUsed = result.getLeasesUsed();
Assert.assertEquals(1, leasesUsed.size());
if(leasesUsed.get(0).cpuCores() == cpus1)
hosts1++;
else
hosts2++;
}
Assert.assertEquals(N, hosts2);
Assert.assertEquals(1, hosts1);
}
// ToDo need a test to confirm BinPackingFitnessCalculators.getCpuAndMemoryBinPacker()
}