ca3568f72d31395bbd2eaa55861f38a797e6b4ec
[osm/SO.git] / rwcal / test / cal_module_test / pytest / cal_module_test.py
1 """
2 #
3 # Copyright 2016 RIFT.IO Inc
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
18 @file cal_test.py
19 @author Varun Prasad (varun.prasad@riftio.com)
20 @date 22-Jan-2016
21
22 """
23
24 import abc
25 import logging
26 import os
27 import multiprocessing
28 import signal
29 import time
30 import uuid
31 import hashlib
32
33 import pytest
34
35 from gi import require_version
36 require_version('RwCal', '1.0')
37
38 from gi.repository import RwcalYang
39 from gi.repository.RwTypes import RwStatus
40 # import rift.cal.server as cal_server
41 import rw_peas
42 import rwlogger
43
44
45 logger = logging.getLogger('rwcal')
46 logging.basicConfig(level=logging.INFO)
47
48
49 class CloudConfig(object):
50 def __init__(self, cal, account):
51 self.cal = cal
52 self.account = account
53
54 def check_state(self, object_id, object_api, expected_state, state_attr_name="state"):
55 """For a given object (Vm, port etc) checks if the object has
56 reached the expected state.
57 """
58 get_object = getattr(self.cal, object_api)
59 for i in range(100): # 100 poll iterations...
60 rc, rs = get_object(self.account, object_id)
61
62 curr_state = getattr(rs, state_attr_name)
63 if curr_state == expected_state:
64 break
65 else:
66 time.sleep(2)
67
68 rc, rs = get_object(self.account, object_id)
69 assert rc == RwStatus.SUCCESS
70 assert getattr(rs, state_attr_name) == expected_state
71
72 def start_server(self):
73 pass
74
75 def stop_server(self):
76 pass
77
78 @abc.abstractmethod
79 def _cal(self):
80 pass
81
82 @abc.abstractmethod
83 def _account(self, option):
84 pass
85
86 @abc.abstractmethod
87 def flavor(self):
88 pass
89
90 @abc.abstractmethod
91 def vdu(self):
92 pass
93
94 @abc.abstractmethod
95 def image(self):
96 pass
97
98 @abc.abstractmethod
99 def virtual_link(self):
100 pass
101
102
103 class Aws(CloudConfig):
104 def __init__(self, option):
105 """
106 Args:
107 option (OptionParser): OptionParser instance.
108 """
109 self.image_id = 'ami-7070231a'
110 self.virtual_link_id = None
111 self.flavor_id = None
112 self.vdu_id = None
113
114 super().__init__(self._cal(), self._account(option))
115
116 def _cal(self):
117 """
118 Loads rw.cal plugin via libpeas
119 """
120 plugin = rw_peas.PeasPlugin('rwcal_aws', 'RwCal-1.0')
121
122 engine, info, extension = plugin()
123
124 # Get the RwLogger context
125 rwloggerctx = rwlogger.RwLog.Ctx.new("Cal-Log")
126
127 cal = plugin.get_interface("Cloud")
128 try:
129 rc = cal.init(rwloggerctx)
130 assert rc == RwStatus.SUCCESS
131 except:
132 logger.error("ERROR:Cal plugin instantiation failed. Aborting tests")
133 else:
134 logger.info("AWS Cal plugin successfully instantiated")
135 return cal
136
137 def _account(self, option):
138 """
139 Args:
140 option (OptionParser): OptionParser instance.
141
142 Return:
143 CloudAccount details
144 """
145 account = RwcalYang.CloudAccount.from_dict({
146 "account_type": "aws",
147 "aws": {
148 "key": option.aws_user,
149 "secret": option.aws_password,
150 "region": option.aws_region,
151 "availability_zone": option.aws_zone,
152 "ssh_key": option.aws_ssh_key
153 }
154 })
155
156 return account
157
158 def flavor(self):
159 """
160 Returns:
161 FlavorInfoItem
162 """
163 flavor = RwcalYang.FlavorInfoItem.from_dict({
164 "name": str(uuid.uuid4()),
165 "vm_flavor": {
166 "memory_mb": 1024,
167 "vcpu_count": 1,
168 "storage_gb": 0
169 }
170 })
171
172 return flavor
173
174 def vdu(self):
175 """Provide AWS specific VDU config.
176
177 Returns:
178 VDUInitParams
179 """
180 vdu = RwcalYang.VDUInitParams.from_dict({
181 "name": str(uuid.uuid4()),
182 "node_id": "123456789012345",
183 "image_id": self.image_id,
184 "flavor_id": "t2.micro"
185 })
186
187 c1 = vdu.connection_points.add()
188 c1.name = str(uuid.uuid4())
189 c1.virtual_link_id = self.virtual_link_id
190
191 return vdu
192
193 def image(self):
194 raise NotImplementedError("Image create APIs are not implemented for AWS")
195
196 def virtual_link(self):
197 """Provide Vlink config
198
199 Returns:
200 VirtualLinkReqParams
201 """
202 vlink = RwcalYang.VirtualLinkReqParams.from_dict({
203 "name": str(uuid.uuid4()),
204 "subnet": '172.31.64.0/20',
205 })
206
207 return vlink
208
209
210 class Cloudsim(CloudConfig):
211 def __init__(self, option):
212 self.image_id = None
213 self.virtual_link_id = None
214 self.flavor_id = None
215 self.vdu_id = None
216
217 self.server_process = None
218
219
220 super().__init__(self._cal(), self._account(option))
221
222 def _md5(fname, blksize=1048576):
223 hash_md5 = hashlib.md5()
224 with open(fname, "rb") as f:
225 for chunk in iter(lambda: f.read(blksize), b""):
226 hash_md5.update(chunk)
227 return hash_md5.hexdigest()
228
229 def start_server(self):
230 logger = logging.getLogger(__name__)
231 server = cal_server.CloudsimServerOperations(logger)
232 self.server_process = multiprocessing.Process(
233 target=server.start_server,
234 args=(True,))
235 self.server_process.start()
236
237 # Sleep till the backup store is set up
238 time.sleep(30)
239
240 def stop_server(self):
241 self.server_process.terminate()
242
243 # If the process is not killed within the timeout, send a SIGKILL.
244 time.sleep(15)
245 if self.server_process.is_alive():
246 os.kill(self.server_process.pid, signal.SIGKILL)
247
248 def _cal(self):
249 """
250 Loads rw.cal plugin via libpeas
251 """
252 plugin = rw_peas.PeasPlugin('rwcal_cloudsimproxy', 'RwCal-1.0')
253 engine, info, extension = plugin()
254
255 # Get the RwLogger context
256 rwloggerctx = rwlogger.RwLog.Ctx.new("Cal-Log")
257
258 cal = plugin.get_interface("Cloud")
259 try:
260 rc = cal.init(rwloggerctx)
261 assert rc == RwStatus.SUCCESS
262 except:
263 logger.error("ERROR:Cal plugin instantiation failed. Aborting tests")
264 else:
265 logger.info("Cloudsim Cal plugin successfully instantiated")
266 return cal
267
268 def _account(self, option):
269 """
270 Args:
271 option (OptionParser): OptionParser instance.
272
273 Return:
274 CloudAccount details
275 """
276 account = RwcalYang.CloudAccount.from_dict({
277 'name': "cloudsim",
278 'account_type':'cloudsim_proxy'})
279
280 return account
281
282 def image(self):
283 """Provides Image config for openstack.
284
285 Returns:
286 ImageInfoItem
287 """
288 image = RwcalYang.ImageInfoItem.from_dict({
289 "name": str(uuid.uuid4()),
290 "location": os.path.join(os.getenv("RIFT_ROOT"), "images/rift-root-latest.qcow2"),
291 "disk_format": "qcow2",
292 "container_format": "bare",
293 "checksum": self._md5(os.path.join(os.getenv("RIFT_ROOT"), "images/rift-root-latest.qcow2")),
294 })
295 return image
296
297 def flavor(self):
298 """Flavor config for openstack
299
300 Returns:
301 FlavorInfoItem
302 """
303 flavor = RwcalYang.FlavorInfoItem.from_dict({
304 "name": str(uuid.uuid4()),
305 "vm_flavor": {
306 "memory_mb": 16392,
307 "vcpu_count": 4,
308 "storage_gb": 40
309 }})
310
311 return flavor
312
313 def vdu(self):
314 """Returns VDU config
315
316 Returns:
317 VDUInitParams
318 """
319 vdu = RwcalYang.VDUInitParams.from_dict({
320 "name": str(uuid.uuid4()),
321 "node_id": "123456789012345",
322 "image_id": self.image_id,
323 "flavor_id": self.flavor_id,
324 })
325
326 c1 = vdu.connection_points.add()
327 c1.name = str(uuid.uuid4())
328 c1.virtual_link_id = self.virtual_link_id
329
330 return vdu
331
332 def virtual_link(self):
333 """vlink config for Openstack
334
335 Returns:
336 VirtualLinkReqParams
337 """
338 vlink = RwcalYang.VirtualLinkReqParams.from_dict({
339 "name": str(uuid.uuid4()),
340 "subnet": '192.168.1.0/24',
341 })
342
343 return vlink
344
345
346 class Openstack(CloudConfig):
347 def __init__(self, option):
348 """
349 Args:
350 option (OptionParser)
351 """
352 self.image_id = None
353 self.virtual_link_id = None
354 self.flavor_id = None
355 self.vdu_id = None
356
357 super().__init__(self._cal(), self._account(option))
358
359 def _cal(self):
360 """
361 Loads rw.cal plugin via libpeas
362 """
363 plugin = rw_peas.PeasPlugin('rwcal_openstack', 'RwCal-1.0')
364 engine, info, extension = plugin()
365
366 # Get the RwLogger context
367 rwloggerctx = rwlogger.RwLog.Ctx.new("Cal-Log")
368
369 cal = plugin.get_interface("Cloud")
370 try:
371 rc = cal.init(rwloggerctx)
372 assert rc == RwStatus.SUCCESS
373 except:
374 logger.error("ERROR:Cal plugin instantiation failed. Aborting tests")
375 else:
376 logger.info("Openstack Cal plugin successfully instantiated")
377 return cal
378
379 def _account(self, option):
380 """Cloud account information for Account
381
382 Returns:
383 CloudAccount
384 """
385 acct = RwcalYang.CloudAccount.from_dict({
386 "account_type": "openstack",
387 "openstack": {
388 "key": option.os_user,
389 "secret": option.os_password,
390 "auth_url": 'http://{}:5000/v3/'.format(option.os_host),
391 "tenant": option.os_tenant,
392 "mgmt_network": option.os_network
393 }
394 })
395
396 return acct
397
398 def _md5(self, fname, blksize=1048576):
399 hash_md5 = hashlib.md5()
400 with open(fname, "rb") as f:
401 for chunk in iter(lambda: f.read(blksize), b""):
402 hash_md5.update(chunk)
403 return hash_md5.hexdigest()
404
405 def image(self):
406 """Provides Image config for openstack.
407
408 Returns:
409 ImageInfoItem
410 """
411 image = RwcalYang.ImageInfoItem.from_dict({
412 "name": str(uuid.uuid4()),
413 "location": os.path.join(os.getenv("RIFT_ROOT"), "images/rift-root-latest.qcow2"),
414 "disk_format": "qcow2",
415 "container_format": "bare",
416 "checksum": self._md5(os.path.join(os.getenv("RIFT_ROOT"), "images/rift-root-latest.qcow2")),
417 })
418 return image
419
420 def flavor(self):
421 """Flavor config for openstack
422
423 Returns:
424 FlavorInfoItem
425 """
426 flavor = RwcalYang.FlavorInfoItem.from_dict({
427 "name": str(uuid.uuid4()),
428 "vm_flavor": {
429 "memory_mb": 16392,
430 "vcpu_count": 4,
431 "storage_gb": 40
432 },
433 "guest_epa": {
434 "cpu_pinning_policy": "DEDICATED",
435 "cpu_thread_pinning_policy": "SEPARATE",
436 }})
437
438 numa_node_count = 2
439 flavor.guest_epa.numa_node_policy.node_cnt = numa_node_count
440 for i in range(numa_node_count):
441 node = flavor.guest_epa.numa_node_policy.node.add()
442 node.id = i
443 if i == 0:
444 node.vcpu = [0, 1]
445 elif i == 1:
446 node.vcpu = [2, 3]
447 node.memory_mb = 8196
448
449 dev = flavor.guest_epa.pcie_device.add()
450 dev.device_id = "PCI_10G_ALIAS"
451 dev.count = 1
452
453 return flavor
454
455 def vdu(self):
456 """Returns VDU config
457
458 Returns:
459 VDUInitParams
460 """
461 vdu = RwcalYang.VDUInitParams.from_dict({
462 "name": str(uuid.uuid4()),
463 "node_id": "123456789012345",
464 "image_id": self.image_id,
465 "flavor_id": self.flavor_id,
466 })
467
468 c1 = vdu.connection_points.add()
469 c1.name = str(uuid.uuid4())
470 c1.virtual_link_id = self.virtual_link_id
471
472 return vdu
473
474 def virtual_link(self):
475 """vlink config for Openstack
476
477 Returns:
478 VirtualLinkReqParams
479 """
480 vlink = RwcalYang.VirtualLinkReqParams.from_dict({
481 "name": str(uuid.uuid4()),
482 "subnet": '192.168.1.0/24',
483 })
484
485 return vlink
486
487
488 @pytest.fixture(scope="module", params=[Openstack], ids=lambda val: val.__name__)
489 def cloud_config(request):
490 return request.param(request.config.option)
491
492
493 @pytest.mark.incremental
494 class TestCalSetup:
495
496 def test_start_server(self, cloud_config):
497 cloud_config.start_server()
498
499 def test_flavor_apis(self, cloud_config):
500 """
501 Asserts:
502 1. If the new flavor is created and available via read APIs
503 2. Verifies the READ APIs
504 """
505 account = cloud_config.account
506 cal = cloud_config.cal
507
508 status, new_flavor_id = cal.create_flavor(account, cloud_config.flavor())
509 cloud_config.flavor_id = new_flavor_id
510 assert status == RwStatus.SUCCESS
511
512 status, flavors = cal.get_flavor_list(account)
513 assert status == RwStatus.SUCCESS
514
515 ids = []
516 for flavor in flavors.flavorinfo_list:
517 status, flavor_single = cal.get_flavor(account, flavor.id)
518 assert status == RwStatus.SUCCESS
519 assert flavor.id == flavor_single.id
520 ids.append(flavor.id)
521
522 assert new_flavor_id in ids
523
524 def test_image_apis(self, cloud_config):
525 """
526 Asserts:
527 1. If the new image is created and available via read APIs
528 2. Verifies the READ APIs
529 """
530 account = cloud_config.account
531 cal = cloud_config.cal
532
533 if type(cloud_config) is Aws:
534 # Hack!
535 new_image_id = "ami-7070231a"
536 else:
537 status, new_image_id = cal.create_image(account, cloud_config.image())
538 cloud_config.image_id = new_image_id
539 assert status == RwStatus.SUCCESS
540 cloud_config.check_state(new_image_id, "get_image", "active")
541
542
543 status, images = cal.get_image_list(account)
544
545 ids = []
546 for image in images.imageinfo_list:
547 status, image_single = cal.get_image(account, image.id)
548 assert status == RwStatus.SUCCESS
549 assert image_single.id == image.id
550 ids.append(image.id)
551
552 assert new_image_id in ids
553
554 def test_virtual_link_create(self, cloud_config):
555 """
556 Asserts:
557 1. If the new Vlink is created and available via read APIs
558 2. Verifies the READ APIs
559 """
560 account = cloud_config.account
561 cal = cloud_config.cal
562
563 status, new_vlink_id = cal.create_virtual_link(account, cloud_config.virtual_link())
564 cloud_config.virtual_link_id = new_vlink_id
565 assert status.status == RwStatus.SUCCESS
566 cloud_config.check_state(new_vlink_id, "get_virtual_link", "active")
567
568 status, vlinks = cal.get_virtual_link_list(account)
569 assert status == RwStatus.SUCCESS
570
571 ids = []
572 for vlink in vlinks.virtual_link_info_list:
573 status, vlink_single = cal.get_virtual_link(account, vlink.virtual_link_id)
574 assert status == RwStatus.SUCCESS
575 assert vlink_single.virtual_link_id == vlink.virtual_link_id
576 ids.append(vlink.virtual_link_id)
577
578 assert new_vlink_id in ids
579
580 def test_vdu_apis(self, cloud_config):
581 """
582 Asserts:
583 1. If the new VDU is created and available via read APIs
584 2. Verifies the READ APIs
585 """
586 account = cloud_config.account
587 cal = cloud_config.cal
588
589 status, new_vdu_id = cal.create_vdu(account, cloud_config.vdu())
590 cloud_config.vdu_id = new_vdu_id
591 assert status.status == RwStatus.SUCCESS
592 cloud_config.check_state(new_vdu_id, "get_vdu", "active")
593
594 status, vdus = cal.get_vdu_list(account)
595 assert status == RwStatus.SUCCESS
596
597 ids = []
598 for vdu in vdus.vdu_info_list:
599 status, vdu_single = cal.get_vdu(account, vdu.vdu_id)
600 assert status == RwStatus.SUCCESS
601 assert vdu_single.vdu_id == vdu.vdu_id
602 ids.append(vdu.vdu_id)
603
604 assert new_vdu_id in ids
605
606 def test_modify_vdu_api(self, cloud_config):
607 account = cloud_config.account
608 cal = cloud_config.cal
609
610 vdu_modify = RwcalYang.VDUModifyParams()
611 vdu_modify.vdu_id = cloud_config.vdu_id
612 c1 = vdu_modify.connection_points_add.add()
613 c1.name = "c_modify1"
614 # Set the new vlink
615 c1.virtual_link_id = cloud_config.virtual_link_id
616
617 status = cal.modify_vdu(account, vdu_modify)
618 assert status == RwStatus.SUCCESS
619
620 @pytest.mark.incremental
621 class TestCalTeardown:
622 def test_flavor_delete(self, cloud_config):
623 """
624 Asserts:
625 1. If flavor is deleted
626 """
627 account = cloud_config.account
628 cal = cloud_config.cal
629
630 if type(cloud_config) != Aws:
631 status = cal.delete_flavor(account, cloud_config.flavor_id)
632 assert status == RwStatus.SUCCESS
633
634 def test_image_delete(self, cloud_config):
635 """
636 Asserts:
637 1. If image is deleted
638 """
639 account = cloud_config.account
640 cal = cloud_config.cal
641
642 if type(cloud_config) != Aws:
643 status = cal.delete_image(account, cloud_config.image_id)
644 assert status == RwStatus.SUCCESS
645
646 def test_virtual_link_delete(self, cloud_config):
647 """
648 Asserts:
649 1. If VLink is deleted
650 """
651 account = cloud_config.account
652 cal = cloud_config.cal
653
654 status = cal.delete_virtual_link(account, cloud_config.virtual_link_id)
655 assert status == RwStatus.SUCCESS
656
657 def test_delete_vdu(self, cloud_config):
658 """
659 Asserts:
660 1. If VDU is deleted
661 """
662 account = cloud_config.account
663 cal = cloud_config.cal
664
665 status = cal.delete_vdu(account, cloud_config.vdu_id)
666 assert status == RwStatus.SUCCESS
667
668 def test_stop_server(self, cloud_config):
669 cloud_config.stop_server()