Merge pull request #133 from mpeuster/master
[osm/vim-emu.git] / src / emuvim / dcemulator / resourcemodel / upb / simple.py
old mode 100644 (file)
new mode 100755 (executable)
index da76b96..5f260d6
@@ -1,10 +1,37 @@
 """
 """
+Copyright (c) 2015 SONATA-NFV and Paderborn University
+ALL RIGHTS RESERVED.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+Neither the name of the SONATA-NFV [, ANY ADDITIONAL AFFILIATION]
+nor the names of its contributors may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+
+This work has been performed in the framework of the SONATA project,
+funded by the European Commission under Grant number 671517 through
+the Horizon 2020 and 5G-PPP programmes. The authors would like to
+acknowledge the contributions of their colleagues of the SONATA
+partner consortium (www.sonata-nfv.eu).
+"""
+"""
 Playground for resource models created by University of Paderborn.
 """
 import time
 import json
 import logging
 Playground for resource models created by University of Paderborn.
 """
 import time
 import json
 import logging
-from emuvim.dcemulator.resourcemodel import BaseResourceModel
+from emuvim.dcemulator.resourcemodel import BaseResourceModel, NotEnoughResourcesAvailable
 
 LOG = logging.getLogger("rm.upb.simple")
 LOG.setLevel(logging.DEBUG)
 
 LOG = logging.getLogger("rm.upb.simple")
 LOG.setLevel(logging.DEBUG)
@@ -37,7 +64,11 @@ class UpbSimpleCloudDcRM(BaseResourceModel):
         self.deactivate_mem_limit = deactivate_mem_limit
         self.single_cu = 0
         self.single_mu = 0
         self.deactivate_mem_limit = deactivate_mem_limit
         self.single_cu = 0
         self.single_mu = 0
-        super(self.__class__, self).__init__()
+        self.cpu_op_factor = 1.0  # over provisioning factor
+        self.mem_op_factor = 1.0
+        self.raise_no_cpu_resources_left = True
+        self.raise_no_mem_resources_left = True
+        super(UpbSimpleCloudDcRM, self).__init__()
 
     def allocate(self, d):
         """
 
     def allocate(self, d):
         """
@@ -61,8 +92,8 @@ class UpbSimpleCloudDcRM(BaseResourceModel):
         """
         fl_cu = self._get_flavor(d).get("compute")
         # check for over provisioning
         """
         fl_cu = self._get_flavor(d).get("compute")
         # check for over provisioning
-        if self.dc_alloc_cu + fl_cu > self.dc_max_cu:
-            raise Exception("Not enough compute resources left.")
+        if self.dc_alloc_cu + fl_cu > self.dc_max_cu and self.raise_no_cpu_resources_left:
+            raise NotEnoughResourcesAvailable("Not enough compute resources left.")
         self.dc_alloc_cu += fl_cu
 
     def _allocate_mem(self, d):
         self.dc_alloc_cu += fl_cu
 
     def _allocate_mem(self, d):
@@ -73,8 +104,8 @@ class UpbSimpleCloudDcRM(BaseResourceModel):
         """
         fl_mu = self._get_flavor(d).get("memory")
         # check for over provisioning
         """
         fl_mu = self._get_flavor(d).get("memory")
         # check for over provisioning
-        if self.dc_alloc_mu + fl_mu > self.dc_max_mu:
-            raise Exception("Not enough memory resources left.")
+        if self.dc_alloc_mu + fl_mu > self.dc_max_mu and self.raise_no_mem_resources_left:
+            raise NotEnoughResourcesAvailable("Not enough memory resources left.")
         self.dc_alloc_mu += fl_mu
 
     def free(self, d):
         self.dc_alloc_mu += fl_mu
 
     def free(self, d):
@@ -110,7 +141,7 @@ class UpbSimpleCloudDcRM(BaseResourceModel):
         """
         Recalculate real resource limits for all allocated containers and apply them
         to their cgroups.
         """
         Recalculate real resource limits for all allocated containers and apply them
         to their cgroups.
-        We have to recalculate for all to allow e.g. overprovisioning models.
+        We have to recalculate for all containers to allow e.g. over provisioning models.
         :return:
         """
         for d in self._allocated_compute_instances.itervalues():
         :return:
         """
         for d in self._allocated_compute_instances.itervalues():
@@ -126,20 +157,28 @@ class UpbSimpleCloudDcRM(BaseResourceModel):
         :return:
         """
         number_cu = self._get_flavor(d).get("compute")
         :return:
         """
         number_cu = self._get_flavor(d).get("compute")
-        # get cpu time fraction for entire emulation
-        e_cpu = self.registrar.e_cpu
         # calculate cpu time fraction of a single compute unit
         # calculate cpu time fraction of a single compute unit
-        self.single_cu = float(e_cpu) / sum([rm.dc_max_cu for rm in list(self.registrar.resource_models)])
+        self.single_cu = self._compute_single_cu()
         # calculate cpu time fraction for container with given flavor
         cpu_time_percentage = self.single_cu * number_cu
         # calculate input values for CFS scheduler bandwidth limitation
         cpu_period, cpu_quota = self._calculate_cpu_cfs_values(cpu_time_percentage)
         # apply limits to container if changed
         if d.cpu_period != cpu_period or d.cpu_quota != cpu_quota:
         # calculate cpu time fraction for container with given flavor
         cpu_time_percentage = self.single_cu * number_cu
         # calculate input values for CFS scheduler bandwidth limitation
         cpu_period, cpu_quota = self._calculate_cpu_cfs_values(cpu_time_percentage)
         # apply limits to container if changed
         if d.cpu_period != cpu_period or d.cpu_quota != cpu_quota:
-            LOG.debug("Setting CPU limit for %r: cpu_quota = cpu_period * limit = %f * %f = %f" % (
-                      d.name, cpu_period, cpu_time_percentage, cpu_quota))
+            LOG.debug("Setting CPU limit for %r: cpu_quota = cpu_period * limit = %f * %f = %f (op_factor=%f)" % (
+                      d.name, cpu_period, cpu_time_percentage, cpu_quota, self.cpu_op_factor))
             d.updateCpuLimit(cpu_period=int(cpu_period), cpu_quota=int(cpu_quota))
 
             d.updateCpuLimit(cpu_period=int(cpu_period), cpu_quota=int(cpu_quota))
 
+    def _compute_single_cu(self):
+        """
+        Calculate percentage of CPU time of a singe CU unit.
+        :return:
+        """
+        # get cpu time fraction for entire emulation
+        e_cpu = self.registrar.e_cpu
+        # calculate
+        return float(e_cpu) / sum([rm.dc_max_cu for rm in list(self.registrar.resource_models)])
+
     def _calculate_cpu_cfs_values(self, cpu_time_percentage):
         """
         Calculate cpu period and quota for CFS
     def _calculate_cpu_cfs_values(self, cpu_time_percentage):
         """
         Calculate cpu period and quota for CFS
@@ -172,7 +211,8 @@ class UpbSimpleCloudDcRM(BaseResourceModel):
         mem_limit = self._calculate_mem_limit_value(mem_limit)
         # apply to container if changed
         if d.mem_limit != mem_limit:
         mem_limit = self._calculate_mem_limit_value(mem_limit)
         # apply to container if changed
         if d.mem_limit != mem_limit:
-            LOG.debug("Setting MEM limit for %r: mem_limit = %f MB" % (d.name, mem_limit/1024/1024))
+            LOG.debug("Setting MEM limit for %r: mem_limit = %f MB (op_factor=%f)" %
+                      (d.name, mem_limit/1024/1024, self.mem_op_factor))
             d.updateMemoryLimit(mem_limit=mem_limit)
 
     def _calculate_mem_limit_value(self, mem_limit):
             d.updateMemoryLimit(mem_limit=mem_limit)
 
     def _calculate_mem_limit_value(self, mem_limit):
@@ -214,6 +254,8 @@ class UpbSimpleCloudDcRM(BaseResourceModel):
         r["dc_alloc_mu"] = self.dc_alloc_mu
         r["single_cu_percentage"] = self.single_cu
         r["single_mu_percentage"] = self.single_mu
         r["dc_alloc_mu"] = self.dc_alloc_mu
         r["single_cu_percentage"] = self.single_cu
         r["single_mu_percentage"] = self.single_mu
+        r["cpu_op_factor"] = self.cpu_op_factor
+        r["mem_op_factor"] = self.mem_op_factor
         r["allocation_state"] = allocation_state
         return r
 
         r["allocation_state"] = allocation_state
         return r
 
@@ -247,3 +289,43 @@ class UpbSimpleCloudDcRM(BaseResourceModel):
         # append to logfile
         with open(path, "a") as f:
             f.write("%s\n" % json.dumps(l))
         # append to logfile
         with open(path, "a") as f:
             f.write("%s\n" % json.dumps(l))
+
+
+class UpbOverprovisioningCloudDcRM(UpbSimpleCloudDcRM):
+    """
+    This will be an example resource model that limits the overall
+    resources that can be deployed per data center.
+    Allows over provisioning. Might result in reducing resources of single
+    containers whenever a data-center is over provisioned.
+    """
+    # TODO add parts for memory
+    def __init__(self, *args, **kvargs):
+        super(UpbOverprovisioningCloudDcRM, self).__init__(*args, **kvargs)
+        self.raise_no_cpu_resources_left = False
+
+    def _compute_single_cu(self):
+        """
+        Calculate percentage of CPU time of a singe CU unit.
+        Take scale-down facte for over provisioning into account.
+        :return:
+        """
+        # get cpu time fraction for entire emulation
+        e_cpu = self.registrar.e_cpu
+        # calculate over provisioning scale factor
+        self.cpu_op_factor = float(self.dc_max_cu) / (max(self.dc_max_cu, self.dc_alloc_cu))
+        # calculate
+        return float(e_cpu) / sum([rm.dc_max_cu for rm in list(self.registrar.resource_models)]) * self.cpu_op_factor
+
+
+class UpbDummyRM(UpbSimpleCloudDcRM):
+    """
+    No limits. But log allocations.
+    """
+    def __init__(self, *args, **kvargs):
+        super(UpbDummyRM, self).__init__(*args, **kvargs)
+        self.raise_no_cpu_resources_left = False
+
+    def _apply_limits(self):
+        # do nothing here
+        pass
+