Merge pull request #68 from mpeuster/master
authorpeusterm <manuel.peuster@uni-paderborn.de>
Fri, 25 Mar 2016 15:10:39 +0000 (16:10 +0100)
committerpeusterm <manuel.peuster@uni-paderborn.de>
Fri, 25 Mar 2016 15:10:39 +0000 (16:10 +0100)
Added memory model to UPB simple resource model

src/emuvim/dcemulator/net.py
src/emuvim/dcemulator/node.py
src/emuvim/dcemulator/resourcemodel/__init__.py
src/emuvim/dcemulator/resourcemodel/upb/simple.py
src/emuvim/test/test_resourcemodel_api.py

index d91ef4a..80fb2f8 100755 (executable)
@@ -27,7 +27,10 @@ class DCNetwork(Dockernet):
     This class is used by topology definition scripts.
     """
 
-    def __init__(self, controller=RemoteController, dc_emulation_max_cpu=1.0, **kwargs):
+    def __init__(self, controller=RemoteController,
+                 dc_emulation_max_cpu=1.0,  # fraction of overall CPU time for emulation
+                 dc_emulation_max_mem=512,  # emulation max mem in MB
+                 **kwargs):
         """
         Create an extended version of a Dockernet network
         :param dc_emulation_max_cpu: max. CPU time used by containers in data centers
@@ -56,7 +59,8 @@ class DCNetwork(Dockernet):
         self.monitor_agent = DCNetworkMonitor(self)
 
         # initialize resource model registrar
-        self.rm_registrar = ResourceModelRegistrar(dc_emulation_max_cpu)
+        self.rm_registrar = ResourceModelRegistrar(
+            dc_emulation_max_cpu, dc_emulation_max_mem)
 
     def addDatacenter(self, label, metadata={}):
         """
index 7746e9e..7a7fa78 100755 (executable)
@@ -156,8 +156,15 @@ class Datacenter(object):
                 # 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
+                    LOG.warning("Increased CPU quota for %r to avoid system error." % name)
+            # check if we have a mem_limit given by the used resource model
+            if mem_limit > 0:
+                LOG.debug(
+                    "MEM limit: mem_limit = %f MB" % mem_limit)
+                # ATTENTION minimum mem_limit per container is 4MB
+                if mem_limit < 4:
+                    mem_limit = 4
+                    LOG.warning("Increased MEM limit for %r because it was less than 4.0 MB." % name)
         # create the container
         d = self.net.addDocker(
             "%s" % (name),
@@ -167,6 +174,8 @@ class Datacenter(object):
             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,
+            mem_limit="%dm" % int(mem_limit) if mem_limit > 0 else None,  # set mem limits if needed
+            memswap_limit="%dm" % int(mem_limit) if mem_limit > 0 else None  # lets set swap to mem limit for now
         )
         # connect all given networks
         for nw in network:
index 3d05b96..e35da25 100644 (file)
@@ -12,11 +12,13 @@ class ResourceModelRegistrar(object):
     Global registry to keep track of all existing resource models.
     """
 
-    def __init__(self, dc_emulation_max_cpu):
+    def __init__(self, dc_emulation_max_cpu, dc_emulation_max_mem):
         self.e_cpu = dc_emulation_max_cpu
+        self.e_mem = dc_emulation_max_mem
         # pointer to all resource models assigned to DCs
         self._resource_models = dict()
-        LOG.info("Resource model registrar created with dc_emulation_max_cpu=%r" % dc_emulation_max_cpu)
+        LOG.info("Resource model registrar created with dc_emulation_max_cpu=%r and dc_emulation_max_mem=%r"
+                 % (dc_emulation_max_cpu, dc_emulation_max_mem))
 
     def register(self, dc, rm):
         """
index f8b8b94..84aa7a9 100644 (file)
@@ -26,6 +26,7 @@ class UpbSimpleCloudDcRM(BaseResourceModel):
         self.dc_max_cu = max_cu
         self.dc_max_mu = max_mu
         self.dc_alloc_cu = 0
+        self.dc_alloc_mu = 0
         super(self.__class__, self).__init__()
 
     def allocate(self, name, flavor_name):
@@ -35,27 +36,13 @@ class UpbSimpleCloudDcRM(BaseResourceModel):
         :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)
+        # calc and return
+        return self._allocate_cpu(fl), self._allocate_mem(fl), -1.0  # return 3tuple (cpu, memory, disk)
 
     def free(self, name):
         """
@@ -66,7 +53,60 @@ class UpbSimpleCloudDcRM(BaseResourceModel):
         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")
+        self._free_cpu(self._flavors.get(self.allocated_compute_instances[name]))
+        self._free_mem(self._flavors.get(self.allocated_compute_instances[name]))
         del self.allocated_compute_instances[name]
         # we don't have to calculate anything special here in this simple model
         return True
+
+    def _allocate_cpu(self, flavor):
+        """
+        Allocate CPU time.
+        :param flavor: flavor dict
+        :return: cpu time fraction
+        """
+        fl_cu = flavor.get("compute")
+        # 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
+        # get cpu time fraction for entire emulation
+        e_cpu = self.registrar.e_cpu
+        # calculate cpu time fraction of a single compute unit
+        cu = float(e_cpu) / sum([rm.dc_max_cu for rm in list(self.registrar.resource_models)])
+        # calculate cpu time fraction for container with given flavor
+        return cu * fl_cu
+
+    def _free_cpu(self, flavor):
+        """
+        Free CPU allocation.
+        :param flavor: flavor dict
+        :return:
+        """
+        self.dc_alloc_cu -= flavor.get("compute")
+
+    def _allocate_mem(self, flavor):
+        """
+        Allocate mem.
+        :param flavor: flavor dict
+        :return: mem limit in MB
+        """
+        fl_mu = flavor.get("memory")
+        # check for over provisioning
+        if self.dc_alloc_mu + fl_mu > self.dc_max_mu:
+            raise Exception("Not enough memory resources left.")
+        self.dc_alloc_mu += fl_mu
+        # get cpu time fraction for entire emulation
+        e_mem = self.registrar.e_mem
+        # calculate cpu time fraction of a single compute unit
+        mu = float(e_mem) / sum([rm.dc_max_mu for rm in list(self.registrar.resource_models)])
+        # calculate cpu time fraction for container with given flavor
+        return mu * fl_mu
+
+    def _free_mem(self, flavor):
+        """
+        Free memory allocation
+        :param flavor: flavor dict
+        :return:
+        """
+        self.dc_alloc_mu -= flavor.get("memory")
index 9b1a3ec..0836c68 100644 (file)
@@ -77,36 +77,53 @@ class testUpbSimpleCloudDcRM(SimpleTestTopology):
         # 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=1.0)
-        rm = UpbSimpleCloudDcRM(max_cu=100, max_mu=100)
+        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)
 
         res = rm.allocate("c1", "tiny")  # calculate allocation
-        self.assertTrue(res[0] == E_CPU / MAX_CU * 1)   # validate compute result
-        self.assertTrue(res[1] < 0)   # validate memory result
+        self.assertEqual(res[0], E_CPU / MAX_CU * 1)   # validate compute result
+        self.assertEqual(res[1], float(E_MEM) / MAX_MU * 32)   # validate memory result
         self.assertTrue(res[2] < 0)   # validate disk result
 
         res = rm.allocate("c2", "small")  # calculate allocation
-        self.assertTrue(res[0] == E_CPU / MAX_CU * 4)   # validate compute result
-        self.assertTrue(res[1] < 0)   # validate memory result
+        self.assertEqual(res[0], E_CPU / MAX_CU * 4)   # validate compute result
+        self.assertEqual(res[1], float(E_MEM) / MAX_MU * 128)   # validate memory result
         self.assertTrue(res[2] < 0)   # validate disk result
 
         res = rm.allocate("c3", "medium")  # calculate allocation
-        self.assertTrue(res[0] == E_CPU / MAX_CU * 8)   # validate compute result
-        self.assertTrue(res[1] < 0)   # validate memory result
+        self.assertEqual(res[0], E_CPU / MAX_CU * 8)   # validate compute result
+        self.assertEqual(res[1], float(E_MEM) / MAX_MU * 256)   # validate memory result
         self.assertTrue(res[2] < 0)   # validate disk result
 
         res = rm.allocate("c4", "large")  # calculate allocation
-        self.assertTrue(res[0] == E_CPU / MAX_CU * 16)   # validate compute result
-        self.assertTrue(res[1] < 0)   # validate memory result
+        self.assertEqual(res[0], E_CPU / MAX_CU * 16)   # validate compute result
+        self.assertEqual(res[1], float(E_MEM) / MAX_MU * 512)   # validate memory result
         self.assertTrue(res[2] < 0)   # validate disk result
 
         res = rm.allocate("c5", "xlarge")  # calculate allocation
-        self.assertTrue(res[0] == E_CPU / MAX_CU * 32)   # validate compute result
-        self.assertTrue(res[1] < 0)   # validate memory result
+        self.assertEqual(res[0], E_CPU / MAX_CU * 32)   # validate compute result
+        self.assertEqual(res[1], float(E_MEM) / MAX_MU * 1024)   # validate memory result
         self.assertTrue(res[2] < 0)   # validate disk result
 
+    def testAllocationCpuLimit(self):
+        """
+        Test CPU allocation limit
+        :return:
+        """
+        # config
+        E_CPU = 1.0
+        MAX_CU = 100
+        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:
@@ -115,7 +132,33 @@ class testUpbSimpleCloudDcRM(SimpleTestTopology):
             rm.allocate("c8", "xlarge")  # calculate allocation
             rm.allocate("c9", "xlarge")  # calculate allocation
         except Exception as e:
-            self.assertTrue("Not enough compute" in e.message)
+            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:
+            rm.allocate("c6", "xlarge")  # calculate allocation
+            rm.allocate("c7", "xlarge")  # calculate allocation
+            rm.allocate("c8", "xlarge")  # calculate allocation
+        except Exception as e:
+            self.assertIn("Not enough memory", e.message)
             exception = True
         self.assertTrue(exception)
 
@@ -128,7 +171,7 @@ class testUpbSimpleCloudDcRM(SimpleTestTopology):
         E_CPU = 1.0
         MAX_CU = 100
         # create dummy resource model environment
-        reg = ResourceModelRegistrar(dc_emulation_max_cpu=1.0)
+        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)
         rm.allocate("c1", "tiny")  # calculate allocation