From 279565d5f8749ac9ac5927a718b7e8e6e63b8450 Mon Sep 17 00:00:00 2001 From: peusterm Date: Sat, 19 Mar 2016 10:36:52 +0100 Subject: [PATCH] Added UPB simple resource model and corresponding tests. Closes #12 --- src/emuvim/dcemulator/node.py | 5 ++ .../dcemulator/resourcemodel/__init__.py | 14 ++++ .../dcemulator/resourcemodel/upb/simple.py | 58 ++++++++++++++-- .../examples/resource_model_demo_topology.py | 9 +-- src/emuvim/test/test_resourcemodel_api.py | 69 +++++++++++++++++++ 5 files changed, 147 insertions(+), 8 deletions(-) diff --git a/src/emuvim/dcemulator/node.py b/src/emuvim/dcemulator/node.py index 9ccdb7f..8de3ae7 100755 --- a/src/emuvim/dcemulator/node.py +++ b/src/emuvim/dcemulator/node.py @@ -192,6 +192,11 @@ class Datacenter(object): } 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 diff --git a/src/emuvim/dcemulator/resourcemodel/__init__.py b/src/emuvim/dcemulator/resourcemodel/__init__.py index 52a35e5..3d05b96 100644 --- a/src/emuvim/dcemulator/resourcemodel/__init__.py +++ b/src/emuvim/dcemulator/resourcemodel/__init__.py @@ -29,12 +29,25 @@ class ResourceModelRegistrar(object): 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): """ @@ -60,6 +73,7 @@ class BaseResourceModel(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) diff --git a/src/emuvim/dcemulator/resourcemodel/upb/simple.py b/src/emuvim/dcemulator/resourcemodel/upb/simple.py index 503e35c..f8b8b94 100644 --- a/src/emuvim/dcemulator/resourcemodel/upb/simple.py +++ b/src/emuvim/dcemulator/resourcemodel/upb/simple.py @@ -8,15 +8,65 @@ LOG = logging.getLogger("rm.upb.simple") 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 diff --git a/src/emuvim/examples/resource_model_demo_topology.py b/src/emuvim/examples/resource_model_demo_topology.py index e65a8ce..ae6ba5e 100644 --- a/src/emuvim/examples/resource_model_demo_topology.py +++ b/src/emuvim/examples/resource_model_demo_topology.py @@ -5,10 +5,11 @@ A simple topology to test resource model support. 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) @@ -16,7 +17,7 @@ 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") @@ -25,8 +26,8 @@ def create_topology1(): 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) diff --git a/src/emuvim/test/test_resourcemodel_api.py b/src/emuvim/test/test_resourcemodel_api.py index 35b4dd5..3412ec4 100644 --- a/src/emuvim/test/test_resourcemodel_api.py +++ b/src/emuvim/test/test_resourcemodel_api.py @@ -1,6 +1,8 @@ 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): @@ -49,3 +51,70 @@ 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) + + + -- 2.17.1