2 Playground for resource models created by University of Paderborn.
7 from emuvim
.dcemulator
.resourcemodel
import BaseResourceModel
9 LOG
= logging
.getLogger("rm.upb.simple")
10 LOG
.setLevel(logging
.DEBUG
)
15 class UpbSimpleCloudDcRM(BaseResourceModel
):
17 This will be an example resource model that limits the overall
18 resources that can be deployed per data center.
19 No over provisioning. Resources are fixed throughout entire container
23 def __init__(self
, max_cu
=32, max_mu
=1024,
24 deactivate_cpu_limit
=False,
25 deactivate_mem_limit
=False):
28 :param max_cu: Maximum number of compute units available in this DC.
29 :param max_mu: Maximum memory of entire dc.
32 self
.dc_max_cu
= max_cu
33 self
.dc_max_mu
= max_mu
36 self
.deactivate_cpu_limit
= deactivate_cpu_limit
37 self
.deactivate_mem_limit
= deactivate_mem_limit
40 self
.raise_no_cpu_resources_left
= True
41 self
.raise_no_mem_resources_left
= True
42 super(UpbSimpleCloudDcRM
, self
).__init
__()
44 def allocate(self
, d
):
46 Allocate resources for the given container.
47 Defined by d.flavor_name
51 self
._allocated
_compute
_instances
[d
.name
] = d
52 if not self
.deactivate_cpu_limit
:
54 if not self
.deactivate_mem_limit
:
58 def _allocate_cpu(self
, d
):
60 Actually allocate (bookkeeping)
64 fl_cu
= self
._get
_flavor
(d
).get("compute")
65 # check for over provisioning
66 if self
.dc_alloc_cu
+ fl_cu
> self
.dc_max_cu
and self
.raise_no_cpu_resources_left
:
67 raise Exception("Not enough compute resources left.")
68 self
.dc_alloc_cu
+= fl_cu
70 def _allocate_mem(self
, d
):
72 Actually allocate (bookkeeping)
76 fl_mu
= self
._get
_flavor
(d
).get("memory")
77 # check for over provisioning
78 if self
.dc_alloc_mu
+ fl_mu
> self
.dc_max_mu
and self
.raise_no_mem_resources_left
:
79 raise Exception("Not enough memory resources left.")
80 self
.dc_alloc_mu
+= fl_mu
84 Free resources allocated to the given container.
88 del self
._allocated
_compute
_instances
[d
.name
]
89 if not self
.deactivate_cpu_limit
:
91 if not self
.deactivate_mem_limit
:
95 def _free_cpu(self
, d
):
101 self
.dc_alloc_cu
-= self
._get
_flavor
(d
).get("compute")
103 def _free_mem(self
, d
):
109 self
.dc_alloc_mu
-= self
._get
_flavor
(d
).get("memory")
111 def _apply_limits(self
):
113 Recalculate real resource limits for all allocated containers and apply them
115 We have to recalculate for all containers to allow e.g. over provisioning models.
118 for d
in self
._allocated
_compute
_instances
.itervalues():
119 if not self
.deactivate_cpu_limit
:
120 self
._apply
_cpu
_limits
(d
)
121 if not self
.deactivate_mem_limit
:
122 self
._apply
_mem
_limits
(d
)
124 def _apply_cpu_limits(self
, d
):
126 Calculate real CPU limit (CFS bandwidth) and apply.
130 number_cu
= self
._get
_flavor
(d
).get("compute")
131 # calculate cpu time fraction of a single compute unit
132 self
.single_cu
= self
._compute
_single
_cu
()
133 # calculate cpu time fraction for container with given flavor
134 cpu_time_percentage
= self
.single_cu
* number_cu
135 # calculate input values for CFS scheduler bandwidth limitation
136 cpu_period
, cpu_quota
= self
._calculate
_cpu
_cfs
_values
(cpu_time_percentage
)
137 # apply limits to container if changed
138 if d
.cpu_period
!= cpu_period
or d
.cpu_quota
!= cpu_quota
:
139 LOG
.debug("Setting CPU limit for %r: cpu_quota = cpu_period * limit = %f * %f = %f" % (
140 d
.name
, cpu_period
, cpu_time_percentage
, cpu_quota
))
141 d
.updateCpuLimit(cpu_period
=int(cpu_period
), cpu_quota
=int(cpu_quota
))
143 def _compute_single_cu(self
):
145 Calculate percentage of CPU time of a singe CU unit.
148 # get cpu time fraction for entire emulation
149 e_cpu
= self
.registrar
.e_cpu
151 return float(e_cpu
) / sum([rm
.dc_max_cu
for rm
in list(self
.registrar
.resource_models
)])
153 def _calculate_cpu_cfs_values(self
, cpu_time_percentage
):
155 Calculate cpu period and quota for CFS
156 :param cpu_time_percentage: percentage of overall CPU to be used
157 :return: cpu_period, cpu_quota
159 # (see: https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt)
160 # Attention minimum cpu_quota is 1ms (micro)
161 cpu_period
= CPU_PERIOD
# lets consider a fixed period of 1000000 microseconds for now
162 cpu_quota
= cpu_period
* cpu_time_percentage
# calculate the fraction of cpu time for this container
163 # ATTENTION >= 1000 to avoid a invalid argument system error ... no idea why
166 LOG
.warning("Increased CPU quota to avoid system error.")
167 return cpu_period
, cpu_quota
169 def _apply_mem_limits(self
, d
):
171 Calculate real mem limit and apply.
175 number_mu
= self
._get
_flavor
(d
).get("memory")
176 # get memory amount for entire emulation
177 e_mem
= self
.registrar
.e_mem
178 # calculate amount of memory for a single mu
179 self
.single_mu
= float(e_mem
) / sum([rm
.dc_max_mu
for rm
in list(self
.registrar
.resource_models
)])
180 # calculate mem for given flavor
181 mem_limit
= self
.single_mu
* number_mu
182 mem_limit
= self
._calculate
_mem
_limit
_value
(mem_limit
)
183 # apply to container if changed
184 if d
.mem_limit
!= mem_limit
:
185 LOG
.debug("Setting MEM limit for %r: mem_limit = %f MB" % (d
.name
, mem_limit
/1024/1024))
186 d
.updateMemoryLimit(mem_limit
=mem_limit
)
188 def _calculate_mem_limit_value(self
, mem_limit
):
190 Calculate actual mem limit as input for cgroup API
191 :param mem_limit: abstract mem limit
192 :return: concrete mem limit
194 # ATTENTION minimum mem_limit per container is 4MB
197 LOG
.warning("Increased MEM limit because it was less than 4.0 MB.")
199 return int(mem_limit
*1024*1024)
201 def get_state_dict(self
):
203 Return the state of the resource model as simple dict.
204 Helper method for logging functionality.
207 # collect info about all allocated instances
208 allocation_state
= dict()
209 for k
, d
in self
._allocated
_compute
_instances
.iteritems():
211 s
["cpu_period"] = d
.cpu_period
212 s
["cpu_quota"] = d
.cpu_quota
213 s
["cpu_shares"] = d
.cpu_shares
214 s
["mem_limit"] = d
.mem_limit
215 s
["memswap_limit"] = d
.memswap_limit
216 allocation_state
[k
] = s
219 r
["e_cpu"] = self
.registrar
.e_cpu
220 r
["e_mem"] = self
.registrar
.e_mem
221 r
["dc_max_cu"] = self
.dc_max_cu
222 r
["dc_max_mu"] = self
.dc_max_mu
223 r
["dc_alloc_cu"] = self
.dc_alloc_cu
224 r
["dc_alloc_mu"] = self
.dc_alloc_mu
225 r
["single_cu_percentage"] = self
.single_cu
226 r
["single_mu_percentage"] = self
.single_mu
227 r
["allocation_state"] = allocation_state
230 def _get_flavor(self
, d
):
232 Get flavor assigned to given container.
233 Identified by d.flavor_name.
237 if d
.flavor_name
not in self
._flavors
:
238 raise Exception("Flavor %r does not exist" % d
.flavor_name
)
239 return self
._flavors
.get(d
.flavor_name
)
241 def _write_log(self
, d
, path
, action
):
243 Helper to log RM info for experiments.
245 :param path: log path
246 :param action: allocate or free
251 # we have a path: write out RM info
254 l
["container_state"] = d
.getStatus()
256 l
["rm_state"] = self
.get_state_dict()
258 with
open(path
, "a") as f
:
259 f
.write("%s\n" % json
.dumps(l
))
262 class UpbOverprovisioningCloudDcRM(UpbSimpleCloudDcRM
):
264 This will be an example resource model that limits the overall
265 resources that can be deployed per data center.
266 Allows over provisioning. Might result in reducing resources of single
267 containers whenever a data-center is over provisioned.
269 def __init__(self
, *args
, **kvargs
):
270 super(UpbOverprovisioningCloudDcRM
, self
).__init
__(*args
, **kvargs
)
271 self
.raise_no_cpu_resources_left
= False
273 def _compute_single_cu(self
):
275 Calculate percentage of CPU time of a singe CU unit.
276 Take scale-down facte for over provisioning into account.
279 # get cpu time fraction for entire emulation
280 e_cpu
= self
.registrar
.e_cpu
281 # calculate over provisioning scale factor
282 self
.op_factor
= float(self
.dc_max_cu
) / (max(self
.dc_max_cu
, self
.dc_alloc_cu
))
283 LOG
.info("========== op_factor=%r ===========" % self
.op_factor
)
285 return float(e_cpu
) / sum([rm
.dc_max_cu
for rm
in list(self
.registrar
.resource_models
)]) * self
.op_factor