network.append({})
# allocate in resource resource model and compute resource limits for new container
+ cpu_limit = mem_limit = disk_limit = -1
+ cpu_period = cpu_quota = None
if self._resource_model is not None:
- # TODO pass resource limits to new container (cf. Dockernet API) Issue #47
+ # call allocate in resource model to calculate resource limit for this container
(cpu_limit, mem_limit, disk_limit) = alloc = self._resource_model.allocate(name, flavor_name)
LOG.debug("Allocation result: %r" % str(alloc))
+ # check if we have a cpu_limit given by the used resource model
+ if cpu_limit > 0:
+ # calculate cpu period and quota for CFS
+ # (see: https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt)
+ # TODO consider multi core machines etc! non trivial!
+ # Attention minimum cpu_quota is 1ms (micro)
+ cpu_period = 100000 # lets consider a fixed period of 100000 microseconds for now
+ cpu_quota = cpu_period * cpu_limit # calculate the fraction of cpu time for this container
+ LOG.debug(
+ "CPU limit: cpu_quota = cpu_period * cpu_limit = %f * %f = %f" % (cpu_period, cpu_limit, cpu_quota))
+ # ATTENTION >= 1000 to avoid a invalid argument system error ... no idea why
+ if cpu_quota < 1000:
+ cpu_quota = 1000
+ LOG.warning("Increased CPU quota for %d to avoid system error." % name)
+ # TODO add memory and disc limitations
# create the container
d = self.net.addDocker(
"%s" % (name),
dimage=image,
dcmd=command,
datacenter=self,
- flavor_name=flavor_name)
+ flavor_name=flavor_name,
+ cpu_period=int(cpu_period) if cpu_limit > 0 else None, # set cpu limits if needed
+ cpu_quota=int(cpu_quota) if cpu_limit > 0 else None,
+ )
# connect all given networks
for nw in network:
# TODO we cannot use TCLink here (see: https://github.com/mpeuster/dockernet/issues/3)
}
def assignResourceModel(self, rm):
+ """
+ Assign a resource model to this DC.
+ :param rm: a BaseResourceModel object
+ :return:
+ """
if self._resource_model is not None:
raise Exception("There is already an resource model assigned to this DC.")
self._resource_model = rm
raise Exception("There is already an resource model assigned to this DC.")
self._resource_models[dc] = rm
rm.registrar = self
+ rm.dcs.append(dc)
LOG.info("Registrar: Added resource model: %r" % rm)
@property
def resource_models(self):
+ """
+ List of registered resource models
+ :return:
+ """
return list(self._resource_models.itervalues())
+ @property
+ def num_dcs_with_rms(self):
+ """
+ Total number of data centers that are connected to a resource model
+ :return:
+ """
+ return sum([len(rm.dcs) for rm in list(self._resource_models.itervalues())])
+
class ResourceFlavor(object):
"""
self._flavors = dict()
self._initDefaultFlavors()
self.registrar = None # pointer to registrar
+ self.dcs = list()
self.allocated_compute_instances = dict()
LOG.info("Resource model %r initialized" % self)
LOG.setLevel(logging.DEBUG)
-class UpbSimpleCloudDcApproxRM(BaseResourceModel):
+class UpbSimpleCloudDcRM(BaseResourceModel):
"""
This will be an example resource model that limits the overall
resources that can be deployed per data center.
+ No over provisioning. Resources are fixed throughout entire container
+ lifetime.
"""
- # TODO Implement resource model issue #12
def __init__(self, max_cu=32, max_mu=1024):
- self._max_cu = max_cu
- self._max_mu = max_mu
+ """
+ Initialize model.
+ :param max_cu: Maximum number of compute units available in this DC.
+ :param max_mu: Maximum memory of entire dc.
+ :return:
+ """
+ self.dc_max_cu = max_cu
+ self.dc_max_mu = max_mu
+ self.dc_alloc_cu = 0
super(self.__class__, self).__init__()
+ def allocate(self, name, flavor_name):
+ """
+ Calculate resources for container with given flavor.
+ :param name: Container name.
+ :param flavor_name: Flavor name.
+ :return:
+ """
+ # TODO Add memory model calculation (split in private methods for each tuple component)
+ # bookkeeping and flavor handling
+ if flavor_name not in self._flavors:
+ raise Exception("Flavor %r does not exist" % flavor_name)
+ fl = self._flavors.get(flavor_name)
+ fl_cu = fl.get("compute")
+ self.allocated_compute_instances[name] = flavor_name
+ # check for over provisioning
+ if self.dc_alloc_cu + fl_cu > self.dc_max_cu:
+ raise Exception("Not enough compute resources left.")
+ self.dc_alloc_cu += fl_cu
+ #
+ # calculate cpu limitation:
+ #
+ # get cpu time fraction for entire emulation
+ e_cpu = self.registrar.e_cpu
+ # calculate cpu time fraction of a single compute unit
+ cu = e_cpu / sum([rm.dc_max_cu for rm in list(self.registrar.resource_models)])
+ # calculate cpu time fraction for container with given flavor
+ c_ct = cu * fl_cu
+ return c_ct, -1.0, -1.0 # return 3tuple (cpu, memory, disk)
+
+ def free(self, name):
+ """
+ Free resources of given container.
+ :param name: Container name.
+ :return:
+ """
+ if name not in self.allocated_compute_instances:
+ return False
+ # bookkeeping
+ self.dc_alloc_cu -= self._flavors.get(self.allocated_compute_instances[name]).get("compute")
+ del self.allocated_compute_instances[name]
+ # we don't have to calculate anything special here in this simple model
+ return True
import logging
import time
from mininet.log import setLogLevel
+from mininet.node import Controller
from emuvim.dcemulator.net import DCNetwork
from emuvim.api.zerorpc.compute import ZeroRpcApiEndpoint
from emuvim.api.sonata import SonataDummyGatekeeperEndpoint
-from emuvim.dcemulator.resourcemodel.upb.simple import UpbSimpleCloudDcApproxRM
+from emuvim.dcemulator.resourcemodel.upb.simple import UpbSimpleCloudDcRM
logging.basicConfig(level=logging.INFO)
def create_topology1():
# create topology
# use a maximum of 50% cpu time for containers added to data centers
- net = DCNetwork(dc_emulation_max_cpu=0.5)
+ net = DCNetwork(dc_emulation_max_cpu=0.5, controller=Controller)
# add some data centers and create a topology
dc1 = net.addDatacenter("dc1")
dc2 = net.addDatacenter("dc2")
net.addLink(dc2, s1, delay="20ms")
# create and assign resource models for each DC
- rm1 = UpbSimpleCloudDcApproxRM(max_cu=10, max_mu=1024)
- rm2 = UpbSimpleCloudDcApproxRM(max_cu=20)
+ rm1 = UpbSimpleCloudDcRM(max_cu=10, max_mu=1024)
+ rm2 = UpbSimpleCloudDcRM(max_cu=20)
dc1.assignResourceModel(rm1)
dc2.assignResourceModel(rm2)
import time
from emuvim.test.base import SimpleTestTopology
from emuvim.dcemulator.resourcemodel import BaseResourceModel, ResourceFlavor
+from emuvim.dcemulator.resourcemodel.upb.simple import UpbSimpleCloudDcRM
+from emuvim.dcemulator.resourcemodel import ResourceModelRegistrar
class testResourceModel(SimpleTestTopology):
assert(self.net.ping([self.h[0], self.h[1]]) <= 0.0)
# stop Mininet network
self.stopNet()
+
+
+class testUpbSimpleCloudDcRM(SimpleTestTopology):
+ """
+ Test the UpbSimpleCloudDc resource model.
+ """
+ def testAllocation(self):
+ # config
+ E_CPU = 1.0
+ MAX_CU = 100
+ # create dummy resource model environment
+ reg = ResourceModelRegistrar(dc_emulation_max_cpu=1.0)
+ rm = UpbSimpleCloudDcRM(max_cu=100, max_mu=100)
+ reg.register("test_dc", rm)
+
+ res = rm.allocate("c1", "tiny") # calculate allocation
+ assert(res[0] == E_CPU / MAX_CU * 1) # validate compute result
+ assert(res[1] < 0) # validate memory result
+ assert(res[2] < 0) # validate disk result
+
+ res = rm.allocate("c2", "small") # calculate allocation
+ assert(res[0] == E_CPU / MAX_CU * 4) # validate compute result
+ assert(res[1] < 0) # validate memory result
+ assert(res[2] < 0) # validate disk result
+
+ res = rm.allocate("c3", "medium") # calculate allocation
+ assert(res[0] == E_CPU / MAX_CU * 8) # validate compute result
+ assert(res[1] < 0) # validate memory result
+ assert(res[2] < 0) # validate disk result
+
+ res = rm.allocate("c4", "large") # calculate allocation
+ assert(res[0] == E_CPU / MAX_CU * 16) # validate compute result
+ assert(res[1] < 0) # validate memory result
+ assert(res[2] < 0) # validate disk result
+
+ res = rm.allocate("c5", "xlarge") # calculate allocation
+ assert(res[0] == E_CPU / MAX_CU * 32) # validate compute result
+ assert(res[1] < 0) # validate memory result
+ assert(res[2] < 0) # validate disk result
+
+ # test over provisioning exeption
+ exception = False
+ try:
+ rm.allocate("c6", "xlarge") # calculate allocation
+ rm.allocate("c7", "xlarge") # calculate allocation
+ rm.allocate("c8", "xlarge") # calculate allocation
+ rm.allocate("c9", "xlarge") # calculate allocation
+ except Exception as e:
+ assert("Not enough compute" in e.message)
+ exception = True
+ assert(exception)
+
+ def testFree(self):
+ # config
+ E_CPU = 1.0
+ MAX_CU = 100
+ # create dummy resource model environment
+ reg = ResourceModelRegistrar(dc_emulation_max_cpu=1.0)
+ rm = UpbSimpleCloudDcRM(max_cu=100, max_mu=100)
+ reg.register("test_dc", rm)
+ rm.allocate("c1", "tiny") # calculate allocation
+ assert(rm.dc_alloc_cu == 1)
+ rm.free("c1")
+ assert(rm.dc_alloc_cu == 0)
+
+
+