c0cf0c45733aa5e322043d198e48f8bda12f3cdd
[osm/vim-emu.git] / src / emuvim / test / unittests / test_resourcemodel.py
1 # Copyright (c) 2015 SONATA-NFV and Paderborn University
2 # ALL RIGHTS RESERVED.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 #
16 # Neither the name of the SONATA-NFV, Paderborn University
17 # nor the names of its contributors may be used to endorse or promote
18 # products derived from this software without specific prior written
19 # permission.
20 #
21 # This work has been performed in the framework of the SONATA project,
22 # funded by the European Commission under Grant number 671517 through
23 # the Horizon 2020 and 5G-PPP programmes. The authors would like to
24 # acknowledge the contributions of their colleagues of the SONATA
25 # partner consortium (www.sonata-nfv.eu).
26 import time
27 import os
28 import unittest
29 from emuvim.test.base import SimpleTestTopology
30 from emuvim.dcemulator.resourcemodel import BaseResourceModel, ResourceFlavor, NotEnoughResourcesAvailable, ResourceModelRegistrar
31 from emuvim.dcemulator.resourcemodel.upb.simple import UpbSimpleCloudDcRM, UpbOverprovisioningCloudDcRM, UpbDummyRM
32
33
34 class testResourceModel(SimpleTestTopology):
35 """
36 Test the general resource model API and functionality.
37 """
38
39 def testBaseResourceModelApi(self):
40 """
41 Tast bare API without real resource madel.
42 :return:
43 """
44 r = BaseResourceModel()
45 # check if default flavors are there
46 self.assertTrue(len(r._flavors) == 5)
47 # check addFlavor functionality
48 f = ResourceFlavor("test", {"testmetric": 42})
49 r.addFlavour(f)
50 self.assertTrue("test" in r._flavors)
51 self.assertTrue(r._flavors.get("test").get("testmetric") == 42)
52
53 def testAddRmToDc(self):
54 """
55 Test is allocate/free is called when a RM is added to a DC.
56 :return:
57 """
58 # create network
59 self.createNet(nswitches=0, ndatacenter=1, nhosts=2, ndockers=0)
60 # setup links
61 self.net.addLink(self.dc[0], self.h[0])
62 self.net.addLink(self.h[1], self.dc[0])
63 # add resource model
64 r = BaseResourceModel()
65 self.dc[0].assignResourceModel(r)
66 # start Mininet network
67 self.startNet()
68 # check number of running nodes
69 self.assertTrue(len(self.getContainernetContainers()) == 0)
70 self.assertTrue(len(self.net.hosts) == 2)
71 self.assertTrue(len(self.net.switches) == 1)
72 # check resource model and resource model registrar
73 self.assertTrue(self.dc[0]._resource_model is not None)
74 self.assertTrue(len(self.net.rm_registrar.resource_models) == 1)
75
76 # check if alloc was called during startCompute
77 self.assertTrue(len(r._allocated_compute_instances) == 0)
78 self.dc[0].startCompute("tc1")
79 time.sleep(1)
80 self.assertTrue(len(r._allocated_compute_instances) == 1)
81 # check if free was called during stopCompute
82 self.dc[0].stopCompute("tc1")
83 self.assertTrue(len(r._allocated_compute_instances) == 0)
84 # check connectivity by using ping
85 self.assertTrue(self.net.ping([self.h[0], self.h[1]]) <= 0.0)
86 # stop Mininet network
87 self.stopNet()
88
89
90 def createDummyContainerObject(name, flavor):
91
92 class DummyContainer(object):
93
94 def __init__(self):
95 # take defaukt values from son-emu
96 self.resources = dict(
97 cpu_period=-1,
98 cpu_quota=-1,
99 mem_limit=-1,
100 memswap_limit=-1
101 )
102 # self.cpu_period = self.resources['cpu_period']
103 # self.cpu_quota = self.resources['cpu_quota']
104 # self.mem_limit = self.resources['mem_limit']
105 # self.memswap_limit = self.resources['memswap_limit']
106
107 def updateCpuLimit(self, cpu_period, cpu_quota):
108 self.resources['cpu_period'] = cpu_period
109 self.resources['cpu_quota'] = cpu_quota
110
111 def updateMemoryLimit(self, mem_limit):
112 self.resources['mem_limit'] = mem_limit
113
114 d = DummyContainer()
115 d.name = name
116 d.flavor_name = flavor
117 return d
118
119
120 class testUpbSimpleCloudDcRM(SimpleTestTopology):
121 """
122 Test the UpbSimpleCloudDc resource model.
123 """
124
125 def testAllocationComputations(self):
126 """
127 Test the allocation procedures and correct calculations.
128 :return:
129 """
130 # config
131 E_CPU = 1.0
132 MAX_CU = 100
133 E_MEM = 512
134 MAX_MU = 2048
135 # create dummy resource model environment
136 reg = ResourceModelRegistrar(
137 dc_emulation_max_cpu=E_CPU, dc_emulation_max_mem=E_MEM)
138 rm = UpbSimpleCloudDcRM(max_cu=MAX_CU, max_mu=MAX_MU)
139 reg.register("test_dc", rm)
140
141 c1 = createDummyContainerObject("c1", flavor="tiny")
142 rm.allocate(c1) # calculate allocation
143 # validate compute result
144 self.assertEqual(float(
145 c1.resources['cpu_quota']) / c1.resources['cpu_period'], E_CPU / MAX_CU * 0.5)
146 # validate memory result
147 self.assertEqual(
148 float(c1.resources['mem_limit'] / 1024 / 1024), float(E_MEM) / MAX_MU * 32)
149
150 c2 = createDummyContainerObject("c2", flavor="small")
151 rm.allocate(c2) # calculate allocation
152 # validate compute result
153 self.assertEqual(float(
154 c2.resources['cpu_quota']) / c2.resources['cpu_period'], E_CPU / MAX_CU * 1)
155 # validate memory result
156 self.assertEqual(
157 float(c2.resources['mem_limit'] / 1024 / 1024), float(E_MEM) / MAX_MU * 128)
158
159 c3 = createDummyContainerObject("c3", flavor="medium")
160 rm.allocate(c3) # calculate allocation
161 # validate compute result
162 self.assertEqual(float(
163 c3.resources['cpu_quota']) / c3.resources['cpu_period'], E_CPU / MAX_CU * 4)
164 # validate memory result
165 self.assertEqual(
166 float(c3.resources['mem_limit'] / 1024 / 1024), float(E_MEM) / MAX_MU * 256)
167
168 c4 = createDummyContainerObject("c4", flavor="large")
169 rm.allocate(c4) # calculate allocation
170 # validate compute result
171 self.assertEqual(float(
172 c4.resources['cpu_quota']) / c4.resources['cpu_period'], E_CPU / MAX_CU * 8)
173 # validate memory result
174 self.assertEqual(
175 float(c4.resources['mem_limit'] / 1024 / 1024), float(E_MEM) / MAX_MU * 512)
176
177 c5 = createDummyContainerObject("c5", flavor="xlarge")
178 rm.allocate(c5) # calculate allocation
179 # validate compute result
180 self.assertEqual(float(
181 c5.resources['cpu_quota']) / c5.resources['cpu_period'], E_CPU / MAX_CU * 16)
182 # validate memory result
183 self.assertEqual(
184 float(c5.resources['mem_limit'] / 1024 / 1024), float(E_MEM) / MAX_MU * 1024)
185
186 def testAllocationCpuLimit(self):
187 """
188 Test CPU allocation limit
189 :return:
190 """
191 # config
192 E_CPU = 1.0
193 MAX_CU = 40
194 E_MEM = 512
195 MAX_MU = 4096
196 # create dummy resource model environment
197 reg = ResourceModelRegistrar(
198 dc_emulation_max_cpu=E_CPU, dc_emulation_max_mem=E_MEM)
199 rm = UpbSimpleCloudDcRM(max_cu=MAX_CU, max_mu=MAX_MU)
200 reg.register("test_dc", rm)
201
202 # test over provisioning exeption
203 exception = False
204 try:
205 c6 = createDummyContainerObject("c6", flavor="xlarge")
206 c7 = createDummyContainerObject("c7", flavor="xlarge")
207 c8 = createDummyContainerObject("c8", flavor="xlarge")
208 c9 = createDummyContainerObject("c9", flavor="xlarge")
209 rm.allocate(c6) # calculate allocation
210 rm.allocate(c7) # calculate allocation
211 rm.allocate(c8) # calculate allocation
212 rm.allocate(c9) # calculate allocation
213 except NotEnoughResourcesAvailable as e:
214 self.assertIn("Not enough compute", e.message)
215 exception = True
216 self.assertTrue(exception)
217
218 def testAllocationMemLimit(self):
219 """
220 Test MEM allocation limit
221 :return:
222 """
223 # config
224 E_CPU = 1.0
225 MAX_CU = 500
226 E_MEM = 512
227 MAX_MU = 2048
228 # create dummy resource model environment
229 reg = ResourceModelRegistrar(
230 dc_emulation_max_cpu=E_CPU, dc_emulation_max_mem=E_MEM)
231 rm = UpbSimpleCloudDcRM(max_cu=MAX_CU, max_mu=MAX_MU)
232 reg.register("test_dc", rm)
233
234 # test over provisioning exeption
235 exception = False
236 try:
237 c6 = createDummyContainerObject("c6", flavor="xlarge")
238 c7 = createDummyContainerObject("c7", flavor="xlarge")
239 c8 = createDummyContainerObject("c8", flavor="xlarge")
240 rm.allocate(c6) # calculate allocation
241 rm.allocate(c7) # calculate allocation
242 rm.allocate(c8) # calculate allocation
243 except NotEnoughResourcesAvailable as e:
244 self.assertIn("Not enough memory", e.message)
245 exception = True
246 self.assertTrue(exception)
247
248 def testFree(self):
249 """
250 Test the free procedure.
251 :return:
252 """
253 # create dummy resource model environment
254 reg = ResourceModelRegistrar(
255 dc_emulation_max_cpu=1.0, dc_emulation_max_mem=512)
256 rm = UpbSimpleCloudDcRM(max_cu=100, max_mu=100)
257 reg.register("test_dc", rm)
258 c1 = createDummyContainerObject("c6", flavor="tiny")
259 rm.allocate(c1) # calculate allocation
260 self.assertTrue(rm.dc_alloc_cu == 0.5)
261 rm.free(c1)
262 self.assertTrue(rm.dc_alloc_cu == 0)
263
264 @unittest.skipIf(os.environ.get("SON_EMU_IN_DOCKER") is not None,
265 "skipping test when running inside Docker container")
266 def testInRealTopo(self):
267 """
268 Start a real container and check if limitations are really passed down to Conteinernet.
269 :return:
270 """
271 # create network
272 self.createNet(nswitches=0, ndatacenter=1, nhosts=2, ndockers=0)
273 # setup links
274 self.net.addLink(self.dc[0], self.h[0])
275 self.net.addLink(self.h[1], self.dc[0])
276 # add resource model
277 r = UpbSimpleCloudDcRM(max_cu=100, max_mu=100)
278 self.dc[0].assignResourceModel(r)
279 # start Mininet network
280 self.startNet()
281 # check number of running nodes
282 self.assertTrue(len(self.getContainernetContainers()) == 0)
283 self.assertTrue(len(self.net.hosts) == 2)
284 self.assertTrue(len(self.net.switches) == 1)
285 # check resource model and resource model registrar
286 self.assertTrue(self.dc[0]._resource_model is not None)
287 self.assertTrue(len(self.net.rm_registrar.resource_models) == 1)
288
289 # check if alloc was called during startCompute
290 self.assertTrue(len(r._allocated_compute_instances) == 0)
291 tc1 = self.dc[0].startCompute("tc1", flavor_name="tiny")
292 time.sleep(1)
293 self.assertTrue(len(r._allocated_compute_instances) == 1)
294
295 # check if there is a real limitation set for containers cgroup
296 # deactivated for now, seems not to work in docker-in-docker setup used
297 # in CI
298 self.assertEqual(
299 float(tc1.resources['cpu_quota']) / tc1.resources['cpu_period'], 0.005)
300
301 # check if free was called during stopCompute
302 self.dc[0].stopCompute("tc1")
303 self.assertTrue(len(r._allocated_compute_instances) == 0)
304 # check connectivity by using ping
305 self.assertTrue(self.net.ping([self.h[0], self.h[1]]) <= 0.0)
306 # stop Mininet network
307 self.stopNet()
308
309
310 class testUpbOverprovisioningCloudDcRM(SimpleTestTopology):
311 """
312 Test the UpbOverprovisioningCloudDc resource model.
313 """
314
315 def testAllocationComputations(self):
316 """
317 Test the allocation procedures and correct calculations.
318 :return:
319 """
320 # config
321 E_CPU = 1.0
322 MAX_CU = 3
323 E_MEM = 512
324 MAX_MU = 2048
325 # create dummy resource model environment
326 reg = ResourceModelRegistrar(
327 dc_emulation_max_cpu=E_CPU, dc_emulation_max_mem=E_MEM)
328 rm = UpbOverprovisioningCloudDcRM(max_cu=MAX_CU, max_mu=MAX_MU)
329 reg.register("test_dc", rm)
330
331 c1 = createDummyContainerObject("c1", flavor="small")
332 rm.allocate(c1) # calculate allocation
333 self.assertAlmostEqual(float(
334 c1.resources['cpu_quota']) / c1.resources['cpu_period'], E_CPU / MAX_CU * 1.0, places=5)
335 self.assertAlmostEqual(
336 float(c1.resources['mem_limit'] / 1024 / 1024), float(E_MEM) / MAX_MU * 128)
337 self.assertAlmostEqual(rm.cpu_op_factor, 1.0)
338
339 c2 = createDummyContainerObject("c2", flavor="small")
340 rm.allocate(c2) # calculate allocation
341 self.assertAlmostEqual(float(
342 c2.resources['cpu_quota']) / c2.resources['cpu_period'], E_CPU / MAX_CU * 1.0, places=5)
343 self.assertAlmostEqual(
344 float(c2.resources['mem_limit'] / 1024 / 1024), float(E_MEM) / MAX_MU * 128)
345 self.assertAlmostEqual(rm.cpu_op_factor, 1.0)
346
347 c3 = createDummyContainerObject("c3", flavor="small")
348 rm.allocate(c3) # calculate allocation
349 self.assertAlmostEqual(float(
350 c3.resources['cpu_quota']) / c3.resources['cpu_period'], E_CPU / MAX_CU * 1.0, places=5)
351 self.assertAlmostEqual(
352 float(c3.resources['mem_limit'] / 1024 / 1024), float(E_MEM) / MAX_MU * 128)
353 self.assertAlmostEqual(rm.cpu_op_factor, 1.0)
354
355 # from this container onwards, we should go to over provisioning mode:
356 c4 = createDummyContainerObject("c4", flavor="small")
357 rm.allocate(c4) # calculate allocation
358 self.assertAlmostEqual(float(
359 c4.resources['cpu_quota']) / c4.resources['cpu_period'], E_CPU / MAX_CU * (float(3) / 4), places=5)
360 self.assertAlmostEqual(float(
361 c4.resources['mem_limit'] / 1024 / 1024), float(E_MEM) / MAX_MU * 128, places=5)
362 self.assertAlmostEqual(rm.cpu_op_factor, 0.75)
363
364 c5 = createDummyContainerObject("c5", flavor="small")
365 rm.allocate(c5) # calculate allocation
366 self.assertAlmostEqual(float(
367 c5.resources['cpu_quota']) / c5.resources['cpu_period'], E_CPU / MAX_CU * (float(3) / 5), places=5)
368 self.assertAlmostEqual(
369 float(c5.resources['mem_limit'] / 1024 / 1024), float(E_MEM) / MAX_MU * 128)
370 self.assertAlmostEqual(rm.cpu_op_factor, 0.6)
371
372
373 class testUpbDummyRM(SimpleTestTopology):
374 """
375 Test the UpbDummyRM resource model.
376 """
377
378 def testAllocationComputations(self):
379 """
380 Test the allocation procedures and correct calculations.
381 :return:
382 """
383 # config
384 E_CPU = 1.0
385 MAX_CU = 3
386 E_MEM = 512
387 MAX_MU = 2048
388 # create dummy resource model environment
389 reg = ResourceModelRegistrar(
390 dc_emulation_max_cpu=E_CPU, dc_emulation_max_mem=E_MEM)
391 rm = UpbDummyRM(max_cu=MAX_CU, max_mu=MAX_MU)
392 reg.register("test_dc", rm)
393
394 c1 = createDummyContainerObject("c1", flavor="small")
395 rm.allocate(c1) # calculate allocation
396 self.assertEqual(len(rm._allocated_compute_instances), 1)
397
398 c2 = createDummyContainerObject("c2", flavor="small")
399 rm.allocate(c2) # calculate allocation
400 self.assertEqual(len(rm._allocated_compute_instances), 2)