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