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