9d5df063c5b4b953816ac8722b55bc23df4ec1cc
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
)
13 class UpbSimpleCloudDcRM(BaseResourceModel
):
15 This will be an example resource model that limits the overall
16 resources that can be deployed per data center.
17 No over provisioning. Resources are fixed throughout entire container
21 def __init__(self
, max_cu
=32, max_mu
=1024,
22 deactivate_cpu_limit
=False,
23 deactivate_mem_limit
=False):
26 :param max_cu: Maximum number of compute units available in this DC.
27 :param max_mu: Maximum memory of entire dc.
30 self
.dc_max_cu
= max_cu
31 self
.dc_max_mu
= max_mu
34 self
.deactivate_cpu_limit
= deactivate_cpu_limit
35 self
.deactivate_mem_limit
= deactivate_mem_limit
36 super(self
.__class
__, self
).__init
__()
38 def allocate(self
, d
):
40 Allocate resources for the given container.
41 Defined by d.flavor_name
45 self
._allocated
_compute
_instances
[d
.name
] = d
46 if not self
.deactivate_cpu_limit
:
48 if not self
.deactivate_mem_limit
:
52 def _allocate_cpu(self
, d
):
54 Actually allocate (bookkeeping)
58 fl_cu
= self
._get
_flavor
(d
).get("compute")
59 # check for over provisioning
60 if self
.dc_alloc_cu
+ fl_cu
> self
.dc_max_cu
:
61 raise Exception("Not enough compute resources left.")
62 self
.dc_alloc_cu
+= fl_cu
64 def _allocate_mem(self
, d
):
66 Actually allocate (bookkeeping)
70 fl_mu
= self
._get
_flavor
(d
).get("memory")
71 # check for over provisioning
72 if self
.dc_alloc_mu
+ fl_mu
> self
.dc_max_mu
:
73 raise Exception("Not enough memory resources left.")
74 self
.dc_alloc_mu
+= fl_mu
78 Free resources allocated to the given container.
82 del self
._allocated
_compute
_instances
[d
.name
]
83 if not self
.deactivate_cpu_limit
:
85 if not self
.deactivate_mem_limit
:
89 def _free_cpu(self
, d
):
95 self
.dc_alloc_cu
-= self
._get
_flavor
(d
).get("compute")
97 def _free_mem(self
, d
):
103 self
.dc_alloc_mu
-= self
._get
_flavor
(d
).get("memory")
105 def _apply_limits(self
):
107 Recalculate real resource limits for all allocated containers and apply them
109 We have to recalculate for all to allow e.g. overprovisioning models.
112 for d
in self
._allocated
_compute
_instances
.itervalues():
113 if not self
.deactivate_cpu_limit
:
114 self
._apply
_cpu
_limits
(d
)
115 if not self
.deactivate_mem_limit
:
116 self
._apply
_mem
_limits
(d
)
118 def _apply_cpu_limits(self
, d
):
120 Calculate real CPU limit (CFS bandwidth) and apply.
124 number_cu
= self
._get
_flavor
(d
).get("compute")
125 # get cpu time fraction for entire emulation
126 e_cpu
= self
.registrar
.e_cpu
127 # calculate cpu time fraction of a single compute unit
128 single_cu
= float(e_cpu
) / sum([rm
.dc_max_cu
for rm
in list(self
.registrar
.resource_models
)])
129 # calculate cpu time fraction for container with given flavor
130 cpu_time_percentage
= single_cu
* number_cu
131 # calculate cpu period and quota for CFS
132 # (see: https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt)
133 # Attention minimum cpu_quota is 1ms (micro)
134 cpu_period
= 1000000 # lets consider a fixed period of 1000000 microseconds for now
135 cpu_quota
= cpu_period
* cpu_time_percentage
# calculate the fraction of cpu time for this container
136 # ATTENTION >= 1000 to avoid a invalid argument system error ... no idea why
139 LOG
.warning("Increased CPU quota for %r to avoid system error." % d
.name
)
140 # apply to container if changed
141 if d
.cpu_period
!= cpu_period
or d
.cpu_quota
!= cpu_quota
:
142 LOG
.debug("Setting CPU limit for %r: cpu_quota = cpu_period * limit = %f * %f = %f" % (
143 d
.name
, cpu_period
, cpu_time_percentage
, cpu_quota
))
144 d
.updateCpuLimit(cpu_period
=int(cpu_period
), cpu_quota
=int(cpu_quota
))
146 def _apply_mem_limits(self
, d
):
148 Calculate real mem limit and apply.
152 number_mu
= self
._get
_flavor
(d
).get("memory")
153 # get memory amount for entire emulation
154 e_mem
= self
.registrar
.e_mem
155 # calculate amount of memory for a single mu
156 single_mu
= float(e_mem
) / sum([rm
.dc_max_mu
for rm
in list(self
.registrar
.resource_models
)])
157 # calculate mem for given flavor
158 mem_limit
= single_mu
* number_mu
159 # ATTENTION minimum mem_limit per container is 4MB
162 LOG
.warning("Increased MEM limit for %r because it was less than 4.0 MB." % d
.name
)
164 mem_limit
= int(mem_limit
*1024*1024)
165 # apply to container if changed
166 if d
.mem_limit
!= mem_limit
:
167 LOG
.debug("Setting MEM limit for %r: mem_limit = %f MB" % (d
.name
, mem_limit
/1024/1024))
168 d
.updateMemoryLimit(mem_limit
=mem_limit
)
170 def get_state_dict(self
):
172 Return the state of the resource model as simple dict.
173 Helper method for logging functionality.
176 # collect info about all allocated instances
177 allocation_state
= dict()
178 for k
, d
in self
._allocated
_compute
_instances
.iteritems():
180 s
["cpu_period"] = d
.cpu_period
181 s
["cpu_quota"] = d
.cpu_quota
182 s
["cpu_shares"] = d
.cpu_shares
183 s
["mem_limit"] = d
.mem_limit
184 s
["memswap_limit"] = d
.memswap_limit
185 allocation_state
[k
] = s
188 r
["e_cpu"] = self
.registrar
.e_cpu
189 r
["e_mem"] = self
.registrar
.e_mem
190 r
["dc_max_cu"] = self
.dc_max_cu
191 r
["dc_max_mu"] = self
.dc_max_mu
192 r
["dc_alloc_cu"] = self
.dc_alloc_cu
193 r
["dc_alloc_mu"] = self
.dc_alloc_mu
194 r
["allocation_state"] = allocation_state
197 def _get_flavor(self
, d
):
199 Get flavor assigned to given container.
200 Identified by d.flavor_name.
204 if d
.flavor_name
not in self
._flavors
:
205 raise Exception("Flavor %r does not exist" % d
.flavor_name
)
206 return self
._flavors
.get(d
.flavor_name
)
208 def _write_log(self
, d
, path
, action
):
210 Helper to log RM info for experiments.
212 :param path: log path
213 :param action: allocate or free
218 # we have a path: write out RM info
221 l
["container_state"] = d
.getStatus()
223 l
["rm_state"] = self
.get_state_dict()
225 with
open(path
, "a") as f
:
226 f
.write("%s\n" % json
.dumps(l
))