blob: b15fed08bfb4696980e80bad512e898d0ff7e045 [file] [log] [blame]
"""
Copyright (c) 2015 SONATA-NFV
ALL RIGHTS RESERVED.
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.
Neither the name of the SONATA-NFV [, ANY ADDITIONAL AFFILIATION]
nor the names of its contributors may be used to endorse or promote
products derived from this software without specific prior written
permission.
This work has been performed in the framework of the SONATA project,
funded by the European Commission under Grant number 671517 through
the Horizon 2020 and 5G-PPP programmes. The authors would like to
acknowledge the contributions of their colleagues of the SONATA
partner consortium (www.sonata-nfv.eu).
"""
import time
import os
import unittest
from emuvim.test.base import SimpleTestTopology
from emuvim.dcemulator.resourcemodel import BaseResourceModel, ResourceFlavor, NotEnoughResourcesAvailable, ResourceModelRegistrar
from emuvim.dcemulator.resourcemodel.upb.simple import UpbSimpleCloudDcRM, UpbOverprovisioningCloudDcRM, UpbDummyRM
class testResourceModel(SimpleTestTopology):
"""
Test the general resource model API and functionality.
"""
def testBaseResourceModelApi(self):
"""
Tast bare API without real resource madel.
:return:
"""
r = BaseResourceModel()
# check if default flavors are there
self.assertTrue(len(r._flavors) == 5)
# check addFlavor functionality
f = ResourceFlavor("test", {"testmetric": 42})
r.addFlavour(f)
self.assertTrue("test" in r._flavors)
self.assertTrue(r._flavors.get("test").get("testmetric") == 42)
def testAddRmToDc(self):
"""
Test is allocate/free is called when a RM is added to a DC.
:return:
"""
# create network
self.createNet(nswitches=0, ndatacenter=1, nhosts=2, ndockers=0)
# setup links
self.net.addLink(self.dc[0], self.h[0])
self.net.addLink(self.h[1], self.dc[0])
# add resource model
r = BaseResourceModel()
self.dc[0].assignResourceModel(r)
# start Mininet network
self.startNet()
# check number of running nodes
self.assertTrue(len(self.getContainernetContainers()) == 0)
self.assertTrue(len(self.net.hosts) == 2)
self.assertTrue(len(self.net.switches) == 1)
# check resource model and resource model registrar
self.assertTrue(self.dc[0]._resource_model is not None)
self.assertTrue(len(self.net.rm_registrar.resource_models) == 1)
# check if alloc was called during startCompute
self.assertTrue(len(r._allocated_compute_instances) == 0)
self.dc[0].startCompute("tc1")
time.sleep(1)
self.assertTrue(len(r._allocated_compute_instances) == 1)
# check if free was called during stopCompute
self.dc[0].stopCompute("tc1")
self.assertTrue(len(r._allocated_compute_instances) == 0)
# check connectivity by using ping
self.assertTrue(self.net.ping([self.h[0], self.h[1]]) <= 0.0)
# stop Mininet network
self.stopNet()
def createDummyContainerObject(name, flavor):
class DummyContainer(object):
def __init__(self):
self.cpu_period = -1
self.cpu_quota = -1
self.mem_limit = -1
self.memswap_limit = -1
def updateCpuLimit(self, cpu_period, cpu_quota):
self.cpu_period = cpu_period
self.cpu_quota = cpu_quota
def updateMemoryLimit(self, mem_limit):
self.mem_limit = mem_limit
d = DummyContainer()
d.name = name
d.flavor_name = flavor
return d
class testUpbSimpleCloudDcRM(SimpleTestTopology):
"""
Test the UpbSimpleCloudDc resource model.
"""
def testAllocationComputations(self):
"""
Test the allocation procedures and correct calculations.
:return:
"""
# config
E_CPU = 1.0
MAX_CU = 100
E_MEM = 512
MAX_MU = 2048
# create dummy resource model environment
reg = ResourceModelRegistrar(dc_emulation_max_cpu=E_CPU, dc_emulation_max_mem=E_MEM)
rm = UpbSimpleCloudDcRM(max_cu=MAX_CU, max_mu=MAX_MU)
reg.register("test_dc", rm)
c1 = createDummyContainerObject("c1", flavor="tiny")
rm.allocate(c1) # calculate allocation
self.assertEqual(float(c1.cpu_quota) / c1.cpu_period, E_CPU / MAX_CU * 0.5) # validate compute result
self.assertEqual(float(c1.mem_limit/1024/1024), float(E_MEM) / MAX_MU * 32) # validate memory result
c2 = createDummyContainerObject("c2", flavor="small")
rm.allocate(c2) # calculate allocation
self.assertEqual(float(c2.cpu_quota) / c2.cpu_period, E_CPU / MAX_CU * 1) # validate compute result
self.assertEqual(float(c2.mem_limit/1024/1024), float(E_MEM) / MAX_MU * 128) # validate memory result
c3 = createDummyContainerObject("c3", flavor="medium")
rm.allocate(c3) # calculate allocation
self.assertEqual(float(c3.cpu_quota) / c3.cpu_period, E_CPU / MAX_CU * 4) # validate compute result
self.assertEqual(float(c3.mem_limit/1024/1024), float(E_MEM) / MAX_MU * 256) # validate memory result
c4 = createDummyContainerObject("c4", flavor="large")
rm.allocate(c4) # calculate allocation
self.assertEqual(float(c4.cpu_quota) / c4.cpu_period, E_CPU / MAX_CU * 8) # validate compute result
self.assertEqual(float(c4.mem_limit/1024/1024), float(E_MEM) / MAX_MU * 512) # validate memory result
c5 = createDummyContainerObject("c5", flavor="xlarge")
rm.allocate(c5) # calculate allocation
self.assertEqual(float(c5.cpu_quota) / c5.cpu_period, E_CPU / MAX_CU * 16) # validate compute result
self.assertEqual(float(c5.mem_limit/1024/1024), float(E_MEM) / MAX_MU * 1024) # validate memory result
def testAllocationCpuLimit(self):
"""
Test CPU allocation limit
:return:
"""
# config
E_CPU = 1.0
MAX_CU = 40
E_MEM = 512
MAX_MU = 4096
# create dummy resource model environment
reg = ResourceModelRegistrar(dc_emulation_max_cpu=E_CPU, dc_emulation_max_mem=E_MEM)
rm = UpbSimpleCloudDcRM(max_cu=MAX_CU, max_mu=MAX_MU)
reg.register("test_dc", rm)
# test over provisioning exeption
exception = False
try:
c6 = createDummyContainerObject("c6", flavor="xlarge")
c7 = createDummyContainerObject("c7", flavor="xlarge")
c8 = createDummyContainerObject("c8", flavor="xlarge")
c9 = createDummyContainerObject("c9", flavor="xlarge")
rm.allocate(c6) # calculate allocation
rm.allocate(c7) # calculate allocation
rm.allocate(c8) # calculate allocation
rm.allocate(c9) # calculate allocation
except NotEnoughResourcesAvailable as e:
self.assertIn("Not enough compute", e.message)
exception = True
self.assertTrue(exception)
def testAllocationMemLimit(self):
"""
Test MEM allocation limit
:return:
"""
# config
E_CPU = 1.0
MAX_CU = 500
E_MEM = 512
MAX_MU = 2048
# create dummy resource model environment
reg = ResourceModelRegistrar(dc_emulation_max_cpu=E_CPU, dc_emulation_max_mem=E_MEM)
rm = UpbSimpleCloudDcRM(max_cu=MAX_CU, max_mu=MAX_MU)
reg.register("test_dc", rm)
# test over provisioning exeption
exception = False
try:
c6 = createDummyContainerObject("c6", flavor="xlarge")
c7 = createDummyContainerObject("c7", flavor="xlarge")
c8 = createDummyContainerObject("c8", flavor="xlarge")
rm.allocate(c6) # calculate allocation
rm.allocate(c7) # calculate allocation
rm.allocate(c8) # calculate allocation
except NotEnoughResourcesAvailable as e:
self.assertIn("Not enough memory", e.message)
exception = True
self.assertTrue(exception)
def testFree(self):
"""
Test the free procedure.
:return:
"""
# config
E_CPU = 1.0
MAX_CU = 100
# create dummy resource model environment
reg = ResourceModelRegistrar(dc_emulation_max_cpu=1.0, dc_emulation_max_mem=512)
rm = UpbSimpleCloudDcRM(max_cu=100, max_mu=100)
reg.register("test_dc", rm)
c1 = createDummyContainerObject("c6", flavor="tiny")
rm.allocate(c1) # calculate allocation
self.assertTrue(rm.dc_alloc_cu == 0.5)
rm.free(c1)
self.assertTrue(rm.dc_alloc_cu == 0)
@unittest.skipIf(os.environ.get("SON_EMU_IN_DOCKER") is not None,
"skipping test when running inside Docker container")
def testInRealTopo(self):
"""
Start a real container and check if limitations are really passed down to Conteinernet.
:return:
"""
# create network
self.createNet(nswitches=0, ndatacenter=1, nhosts=2, ndockers=0)
# setup links
self.net.addLink(self.dc[0], self.h[0])
self.net.addLink(self.h[1], self.dc[0])
# add resource model
r = UpbSimpleCloudDcRM(max_cu=100, max_mu=100)
self.dc[0].assignResourceModel(r)
# start Mininet network
self.startNet()
# check number of running nodes
self.assertTrue(len(self.getContainernetContainers()) == 0)
self.assertTrue(len(self.net.hosts) == 2)
self.assertTrue(len(self.net.switches) == 1)
# check resource model and resource model registrar
self.assertTrue(self.dc[0]._resource_model is not None)
self.assertTrue(len(self.net.rm_registrar.resource_models) == 1)
# check if alloc was called during startCompute
self.assertTrue(len(r._allocated_compute_instances) == 0)
tc1 = self.dc[0].startCompute("tc1", flavor_name="tiny")
time.sleep(1)
self.assertTrue(len(r._allocated_compute_instances) == 1)
# check if there is a real limitation set for containers cgroup
# deactivated for now, seems not to work in docker-in-docker setup used in CI
self.assertEqual(float(tc1.cpu_quota)/tc1.cpu_period, 0.005)
# check if free was called during stopCompute
self.dc[0].stopCompute("tc1")
self.assertTrue(len(r._allocated_compute_instances) == 0)
# check connectivity by using ping
self.assertTrue(self.net.ping([self.h[0], self.h[1]]) <= 0.0)
# stop Mininet network
self.stopNet()
class testUpbOverprovisioningCloudDcRM(SimpleTestTopology):
"""
Test the UpbOverprovisioningCloudDc resource model.
"""
def testAllocationComputations(self):
"""
Test the allocation procedures and correct calculations.
:return:
"""
# config
E_CPU = 1.0
MAX_CU = 3
E_MEM = 512
MAX_MU = 2048
# create dummy resource model environment
reg = ResourceModelRegistrar(dc_emulation_max_cpu=E_CPU, dc_emulation_max_mem=E_MEM)
rm = UpbOverprovisioningCloudDcRM(max_cu=MAX_CU, max_mu=MAX_MU)
reg.register("test_dc", rm)
c1 = createDummyContainerObject("c1", flavor="small")
rm.allocate(c1) # calculate allocation
self.assertAlmostEqual(float(c1.cpu_quota) / c1.cpu_period, E_CPU / MAX_CU * 1.0, places=5)
self.assertAlmostEqual(float(c1.mem_limit/1024/1024), float(E_MEM) / MAX_MU * 128)
self.assertAlmostEqual(rm.cpu_op_factor, 1.0)
c2 = createDummyContainerObject("c2", flavor="small")
rm.allocate(c2) # calculate allocation
self.assertAlmostEqual(float(c2.cpu_quota) / c2.cpu_period, E_CPU / MAX_CU * 1.0, places=5)
self.assertAlmostEqual(float(c2.mem_limit/1024/1024), float(E_MEM) / MAX_MU * 128)
self.assertAlmostEqual(rm.cpu_op_factor, 1.0)
c3 = createDummyContainerObject("c3", flavor="small")
rm.allocate(c3) # calculate allocation
self.assertAlmostEqual(float(c3.cpu_quota) / c3.cpu_period, E_CPU / MAX_CU * 1.0, places=5)
self.assertAlmostEqual(float(c3.mem_limit/1024/1024), float(E_MEM) / MAX_MU * 128)
self.assertAlmostEqual(rm.cpu_op_factor, 1.0)
# from this container onwards, we should go to over provisioning mode:
c4 = createDummyContainerObject("c4", flavor="small")
rm.allocate(c4) # calculate allocation
self.assertAlmostEqual(float(c4.cpu_quota) / c4.cpu_period, E_CPU / MAX_CU * (float(3) / 4), places=5)
self.assertAlmostEqual(float(c4.mem_limit/1024/1024), float(E_MEM) / MAX_MU * 128, places=5)
self.assertAlmostEqual(rm.cpu_op_factor, 0.75)
c5 = createDummyContainerObject("c5", flavor="small")
rm.allocate(c5) # calculate allocation
self.assertAlmostEqual(float(c5.cpu_quota) / c5.cpu_period, E_CPU / MAX_CU * (float(3) / 5), places=5)
self.assertAlmostEqual(float(c5.mem_limit/1024/1024), float(E_MEM) / MAX_MU * 128)
self.assertAlmostEqual(rm.cpu_op_factor, 0.6)
class testUpbDummyRM(SimpleTestTopology):
"""
Test the UpbDummyRM resource model.
"""
def testAllocationComputations(self):
"""
Test the allocation procedures and correct calculations.
:return:
"""
# config
E_CPU = 1.0
MAX_CU = 3
E_MEM = 512
MAX_MU = 2048
# create dummy resource model environment
reg = ResourceModelRegistrar(dc_emulation_max_cpu=E_CPU, dc_emulation_max_mem=E_MEM)
rm = UpbDummyRM(max_cu=MAX_CU, max_mu=MAX_MU)
reg.register("test_dc", rm)
c1 = createDummyContainerObject("c1", flavor="small")
rm.allocate(c1) # calculate allocation
self.assertEqual(len(rm._allocated_compute_instances), 1)
c2 = createDummyContainerObject("c2", flavor="small")
rm.allocate(c2) # calculate allocation
self.assertEqual(len(rm._allocated_compute_instances), 2)