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):
         }
 
     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
         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
             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):
         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())
 
         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):
     """
 
 class ResourceFlavor(object):
     """
@@ -60,6 +73,7 @@ class BaseResourceModel(object):
         self._flavors = dict()
         self._initDefaultFlavors()
         self.registrar = None  # pointer to registrar
         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)
 
         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)
 
 
 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.
     """
     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):
 
     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__()
 
         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
 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.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)
 
 
 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
 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")
     # 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
     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)
 
     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
 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):
 
 
 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()
         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)
+
+
+