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 super(self
.__class
__, self
).__init
__()
42 def allocate(self
, d
):
44 Allocate resources for the given container.
45 Defined by d.flavor_name
49 self
._allocated
_compute
_instances
[d
.name
] = d
50 if not self
.deactivate_cpu_limit
:
52 if not self
.deactivate_mem_limit
:
56 def _allocate_cpu(self
, d
):
58 Actually allocate (bookkeeping)
62 fl_cu
= self
._get
_flavor
(d
).get("compute")
63 # check for over provisioning
64 if self
.dc_alloc_cu
+ fl_cu
> self
.dc_max_cu
:
65 raise Exception("Not enough compute resources left.")
66 self
.dc_alloc_cu
+= fl_cu
68 def _allocate_mem(self
, d
):
70 Actually allocate (bookkeeping)
74 fl_mu
= self
._get
_flavor
(d
).get("memory")
75 # check for over provisioning
76 if self
.dc_alloc_mu
+ fl_mu
> self
.dc_max_mu
:
77 raise Exception("Not enough memory resources left.")
78 self
.dc_alloc_mu
+= fl_mu
82 Free resources allocated to the given container.
86 del self
._allocated
_compute
_instances
[d
.name
]
87 if not self
.deactivate_cpu_limit
:
89 if not self
.deactivate_mem_limit
:
93 def _free_cpu(self
, d
):
99 self
.dc_alloc_cu
-= self
._get
_flavor
(d
).get("compute")
101 def _free_mem(self
, d
):
107 self
.dc_alloc_mu
-= self
._get
_flavor
(d
).get("memory")
109 def _apply_limits(self
):
111 Recalculate real resource limits for all allocated containers and apply them
113 We have to recalculate for all to allow e.g. overprovisioning models.
116 for d
in self
._allocated
_compute
_instances
.itervalues():
117 if not self
.deactivate_cpu_limit
:
118 self
._apply
_cpu
_limits
(d
)
119 if not self
.deactivate_mem_limit
:
120 self
._apply
_mem
_limits
(d
)
122 def _apply_cpu_limits(self
, d
):
124 Calculate real CPU limit (CFS bandwidth) and apply.
128 number_cu
= self
._get
_flavor
(d
).get("compute")
129 # calculate cpu time fraction of a single compute unit
130 self
.single_cu
= self
._compute
_single
_cu
()
131 # calculate cpu time fraction for container with given flavor
132 cpu_time_percentage
= self
.single_cu
* number_cu
133 # calculate input values for CFS scheduler bandwidth limitation
134 cpu_period
, cpu_quota
= self
._calculate
_cpu
_cfs
_values
(cpu_time_percentage
)
135 # apply limits to container if changed
136 if d
.cpu_period
!= cpu_period
or d
.cpu_quota
!= cpu_quota
:
137 LOG
.debug("Setting CPU limit for %r: cpu_quota = cpu_period * limit = %f * %f = %f" % (
138 d
.name
, cpu_period
, cpu_time_percentage
, cpu_quota
))
139 d
.updateCpuLimit(cpu_period
=int(cpu_period
), cpu_quota
=int(cpu_quota
))
141 def _compute_single_cu(self
):
143 Calculate percentage of CPU time of a singe CU unit.
146 # get cpu time fraction for entire emulation
147 e_cpu
= self
.registrar
.e_cpu
149 return float(e_cpu
) / sum([rm
.dc_max_cu
for rm
in list(self
.registrar
.resource_models
)])
151 def _calculate_cpu_cfs_values(self
, cpu_time_percentage
):
153 Calculate cpu period and quota for CFS
154 :param cpu_time_percentage: percentage of overall CPU to be used
155 :return: cpu_period, cpu_quota
157 # (see: https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt)
158 # Attention minimum cpu_quota is 1ms (micro)
159 cpu_period
= CPU_PERIOD
# lets consider a fixed period of 1000000 microseconds for now
160 cpu_quota
= cpu_period
* cpu_time_percentage
# calculate the fraction of cpu time for this container
161 # ATTENTION >= 1000 to avoid a invalid argument system error ... no idea why
164 LOG
.warning("Increased CPU quota to avoid system error.")
165 return cpu_period
, cpu_quota
167 def _apply_mem_limits(self
, d
):
169 Calculate real mem limit and apply.
173 number_mu
= self
._get
_flavor
(d
).get("memory")
174 # get memory amount for entire emulation
175 e_mem
= self
.registrar
.e_mem
176 # calculate amount of memory for a single mu
177 self
.single_mu
= float(e_mem
) / sum([rm
.dc_max_mu
for rm
in list(self
.registrar
.resource_models
)])
178 # calculate mem for given flavor
179 mem_limit
= self
.single_mu
* number_mu
180 mem_limit
= self
._calculate
_mem
_limit
_value
(mem_limit
)
181 # apply to container if changed
182 if d
.mem_limit
!= mem_limit
:
183 LOG
.debug("Setting MEM limit for %r: mem_limit = %f MB" % (d
.name
, mem_limit
/1024/1024))
184 d
.updateMemoryLimit(mem_limit
=mem_limit
)
186 def _calculate_mem_limit_value(self
, mem_limit
):
188 Calculate actual mem limit as input for cgroup API
189 :param mem_limit: abstract mem limit
190 :return: concrete mem limit
192 # ATTENTION minimum mem_limit per container is 4MB
195 LOG
.warning("Increased MEM limit because it was less than 4.0 MB.")
197 return int(mem_limit
*1024*1024)
199 def get_state_dict(self
):
201 Return the state of the resource model as simple dict.
202 Helper method for logging functionality.
205 # collect info about all allocated instances
206 allocation_state
= dict()
207 for k
, d
in self
._allocated
_compute
_instances
.iteritems():
209 s
["cpu_period"] = d
.cpu_period
210 s
["cpu_quota"] = d
.cpu_quota
211 s
["cpu_shares"] = d
.cpu_shares
212 s
["mem_limit"] = d
.mem_limit
213 s
["memswap_limit"] = d
.memswap_limit
214 allocation_state
[k
] = s
217 r
["e_cpu"] = self
.registrar
.e_cpu
218 r
["e_mem"] = self
.registrar
.e_mem
219 r
["dc_max_cu"] = self
.dc_max_cu
220 r
["dc_max_mu"] = self
.dc_max_mu
221 r
["dc_alloc_cu"] = self
.dc_alloc_cu
222 r
["dc_alloc_mu"] = self
.dc_alloc_mu
223 r
["single_cu_percentage"] = self
.single_cu
224 r
["single_mu_percentage"] = self
.single_mu
225 r
["allocation_state"] = allocation_state
228 def _get_flavor(self
, d
):
230 Get flavor assigned to given container.
231 Identified by d.flavor_name.
235 if d
.flavor_name
not in self
._flavors
:
236 raise Exception("Flavor %r does not exist" % d
.flavor_name
)
237 return self
._flavors
.get(d
.flavor_name
)
239 def _write_log(self
, d
, path
, action
):
241 Helper to log RM info for experiments.
243 :param path: log path
244 :param action: allocate or free
249 # we have a path: write out RM info
252 l
["container_state"] = d
.getStatus()
254 l
["rm_state"] = self
.get_state_dict()
256 with
open(path
, "a") as f
:
257 f
.write("%s\n" % json
.dumps(l
))