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
.cpu_op_factor
= 1.0 # over provisioning factor
41 self
.mem_op_factor
= 1.0
42 self
.raise_no_cpu_resources_left
= True
43 self
.raise_no_mem_resources_left
= True
44 super(UpbSimpleCloudDcRM
, self
).__init
__()
46 def allocate(self
, d
):
48 Allocate resources for the given container.
49 Defined by d.flavor_name
53 self
._allocated
_compute
_instances
[d
.name
] = d
54 if not self
.deactivate_cpu_limit
:
56 if not self
.deactivate_mem_limit
:
60 def _allocate_cpu(self
, d
):
62 Actually allocate (bookkeeping)
66 fl_cu
= self
._get
_flavor
(d
).get("compute")
67 # check for over provisioning
68 if self
.dc_alloc_cu
+ fl_cu
> self
.dc_max_cu
and self
.raise_no_cpu_resources_left
:
69 raise Exception("Not enough compute resources left.")
70 self
.dc_alloc_cu
+= fl_cu
72 def _allocate_mem(self
, d
):
74 Actually allocate (bookkeeping)
78 fl_mu
= self
._get
_flavor
(d
).get("memory")
79 # check for over provisioning
80 if self
.dc_alloc_mu
+ fl_mu
> self
.dc_max_mu
and self
.raise_no_mem_resources_left
:
81 raise Exception("Not enough memory resources left.")
82 self
.dc_alloc_mu
+= fl_mu
86 Free resources allocated to the given container.
90 del self
._allocated
_compute
_instances
[d
.name
]
91 if not self
.deactivate_cpu_limit
:
93 if not self
.deactivate_mem_limit
:
97 def _free_cpu(self
, d
):
103 self
.dc_alloc_cu
-= self
._get
_flavor
(d
).get("compute")
105 def _free_mem(self
, d
):
111 self
.dc_alloc_mu
-= self
._get
_flavor
(d
).get("memory")
113 def _apply_limits(self
):
115 Recalculate real resource limits for all allocated containers and apply them
117 We have to recalculate for all containers to allow e.g. over provisioning models.
120 for d
in self
._allocated
_compute
_instances
.itervalues():
121 if not self
.deactivate_cpu_limit
:
122 self
._apply
_cpu
_limits
(d
)
123 if not self
.deactivate_mem_limit
:
124 self
._apply
_mem
_limits
(d
)
126 def _apply_cpu_limits(self
, d
):
128 Calculate real CPU limit (CFS bandwidth) and apply.
132 number_cu
= self
._get
_flavor
(d
).get("compute")
133 # calculate cpu time fraction of a single compute unit
134 self
.single_cu
= self
._compute
_single
_cu
()
135 # calculate cpu time fraction for container with given flavor
136 cpu_time_percentage
= self
.single_cu
* number_cu
137 # calculate input values for CFS scheduler bandwidth limitation
138 cpu_period
, cpu_quota
= self
._calculate
_cpu
_cfs
_values
(cpu_time_percentage
)
139 # apply limits to container if changed
140 if d
.cpu_period
!= cpu_period
or d
.cpu_quota
!= cpu_quota
:
141 LOG
.debug("Setting CPU limit for %r: cpu_quota = cpu_period * limit = %f * %f = %f (op_factor=%f)" % (
142 d
.name
, cpu_period
, cpu_time_percentage
, cpu_quota
, self
.cpu_op_factor
))
143 d
.updateCpuLimit(cpu_period
=int(cpu_period
), cpu_quota
=int(cpu_quota
))
145 def _compute_single_cu(self
):
147 Calculate percentage of CPU time of a singe CU unit.
150 # get cpu time fraction for entire emulation
151 e_cpu
= self
.registrar
.e_cpu
153 return float(e_cpu
) / sum([rm
.dc_max_cu
for rm
in list(self
.registrar
.resource_models
)])
155 def _calculate_cpu_cfs_values(self
, cpu_time_percentage
):
157 Calculate cpu period and quota for CFS
158 :param cpu_time_percentage: percentage of overall CPU to be used
159 :return: cpu_period, cpu_quota
161 # (see: https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt)
162 # Attention minimum cpu_quota is 1ms (micro)
163 cpu_period
= CPU_PERIOD
# lets consider a fixed period of 1000000 microseconds for now
164 cpu_quota
= cpu_period
* cpu_time_percentage
# calculate the fraction of cpu time for this container
165 # ATTENTION >= 1000 to avoid a invalid argument system error ... no idea why
168 LOG
.warning("Increased CPU quota to avoid system error.")
169 return cpu_period
, cpu_quota
171 def _apply_mem_limits(self
, d
):
173 Calculate real mem limit and apply.
177 number_mu
= self
._get
_flavor
(d
).get("memory")
178 # get memory amount for entire emulation
179 e_mem
= self
.registrar
.e_mem
180 # calculate amount of memory for a single mu
181 self
.single_mu
= float(e_mem
) / sum([rm
.dc_max_mu
for rm
in list(self
.registrar
.resource_models
)])
182 # calculate mem for given flavor
183 mem_limit
= self
.single_mu
* number_mu
184 mem_limit
= self
._calculate
_mem
_limit
_value
(mem_limit
)
185 # apply to container if changed
186 if d
.mem_limit
!= mem_limit
:
187 LOG
.debug("Setting MEM limit for %r: mem_limit = %f MB (op_factor=%f)" %
188 (d
.name
, mem_limit
/1024/1024, self
.mem_op_factor
))
189 d
.updateMemoryLimit(mem_limit
=mem_limit
)
191 def _calculate_mem_limit_value(self
, mem_limit
):
193 Calculate actual mem limit as input for cgroup API
194 :param mem_limit: abstract mem limit
195 :return: concrete mem limit
197 # ATTENTION minimum mem_limit per container is 4MB
200 LOG
.warning("Increased MEM limit because it was less than 4.0 MB.")
202 return int(mem_limit
*1024*1024)
204 def get_state_dict(self
):
206 Return the state of the resource model as simple dict.
207 Helper method for logging functionality.
210 # collect info about all allocated instances
211 allocation_state
= dict()
212 for k
, d
in self
._allocated
_compute
_instances
.iteritems():
214 s
["cpu_period"] = d
.cpu_period
215 s
["cpu_quota"] = d
.cpu_quota
216 s
["cpu_shares"] = d
.cpu_shares
217 s
["mem_limit"] = d
.mem_limit
218 s
["memswap_limit"] = d
.memswap_limit
219 allocation_state
[k
] = s
222 r
["e_cpu"] = self
.registrar
.e_cpu
223 r
["e_mem"] = self
.registrar
.e_mem
224 r
["dc_max_cu"] = self
.dc_max_cu
225 r
["dc_max_mu"] = self
.dc_max_mu
226 r
["dc_alloc_cu"] = self
.dc_alloc_cu
227 r
["dc_alloc_mu"] = self
.dc_alloc_mu
228 r
["single_cu_percentage"] = self
.single_cu
229 r
["single_mu_percentage"] = self
.single_mu
230 r
["cpu_op_factor"] = self
.cpu_op_factor
231 r
["mem_op_factor"] = self
.mem_op_factor
232 r
["allocation_state"] = allocation_state
235 def _get_flavor(self
, d
):
237 Get flavor assigned to given container.
238 Identified by d.flavor_name.
242 if d
.flavor_name
not in self
._flavors
:
243 raise Exception("Flavor %r does not exist" % d
.flavor_name
)
244 return self
._flavors
.get(d
.flavor_name
)
246 def _write_log(self
, d
, path
, action
):
248 Helper to log RM info for experiments.
250 :param path: log path
251 :param action: allocate or free
256 # we have a path: write out RM info
259 l
["container_state"] = d
.getStatus()
261 l
["rm_state"] = self
.get_state_dict()
263 with
open(path
, "a") as f
:
264 f
.write("%s\n" % json
.dumps(l
))
267 class UpbOverprovisioningCloudDcRM(UpbSimpleCloudDcRM
):
269 This will be an example resource model that limits the overall
270 resources that can be deployed per data center.
271 Allows over provisioning. Might result in reducing resources of single
272 containers whenever a data-center is over provisioned.
274 # TODO add parts for memory
275 def __init__(self
, *args
, **kvargs
):
276 super(UpbOverprovisioningCloudDcRM
, self
).__init
__(*args
, **kvargs
)
277 self
.raise_no_cpu_resources_left
= False
279 def _compute_single_cu(self
):
281 Calculate percentage of CPU time of a singe CU unit.
282 Take scale-down facte for over provisioning into account.
285 # get cpu time fraction for entire emulation
286 e_cpu
= self
.registrar
.e_cpu
287 # calculate over provisioning scale factor
288 self
.cpu_op_factor
= float(self
.dc_max_cu
) / (max(self
.dc_max_cu
, self
.dc_alloc_cu
))
290 return float(e_cpu
) / sum([rm
.dc_max_cu
for rm
in list(self
.registrar
.resource_models
)]) * self
.cpu_op_factor