Added UPB simple resource model and corresponding tests. Closes #12
authorpeusterm <manuel.peuster@uni-paderborn.de>
Sat, 19 Mar 2016 09:36:52 +0000 (10:36 +0100)
committerpeusterm <manuel.peuster@uni-paderborn.de>
Sat, 19 Mar 2016 09:36:52 +0000 (10:36 +0100)
src/emuvim/dcemulator/node.py
src/emuvim/dcemulator/resourcemodel/__init__.py
src/emuvim/dcemulator/resourcemodel/upb/simple.py
src/emuvim/examples/resource_model_demo_topology.py
src/emuvim/test/test_resourcemodel_api.py

index 9ccdb7f..8de3ae7 100755 (executable)
@@ -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
index 52a35e5..3d05b96 100644 (file)
@@ -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)
 
index 503e35c..f8b8b94 100644 (file)
@@ -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
index e65a8ce..ae6ba5e 100644 (file)
@@ -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)
 
index 35b4dd5..3412ec4 100644 (file)
@@ -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)
+
+
+