RIFT-15099 Model changes and implementation for passing meta-data and custom files...
[osm/SO.git] / rwcal / test / test_rwcal_openstack.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 import datetime
19 import logging
20 import time
21 import unittest
22 import hashlib
23
24 import novaclient.exceptions as nova_exception
25 import paramiko
26 import rw_peas
27 import rwlogger
28 from keystoneclient import v3 as ksclient
29
30 from gi.repository import RwcalYang
31 from gi.repository.RwTypes import RwStatus
32 from rift.rwcal.openstack.openstack_drv import KeystoneDriver, NovaDriver
33
34 logger = logging.getLogger('rwcal-openstack')
35
36 PING_USERDATA = '''
37 #cloud-config
38 password: fedora
39 chpasswd: { expire: False }
40 ssh_pwauth: True
41 '''
42
43 #
44 # Important information about openstack installation. This needs to be manually verified
45 #
46 openstack_info = {
47 'username' : 'pluto',
48 'password' : 'mypasswd',
49 'auth_url' : 'http://10.66.4.17:5000/v3/',
50 'project_name' : 'demo',
51 'mgmt_network' : 'private',
52 'reserved_flavor' : 'm1.medium',
53 'reserved_image' : 'Fedora-x86_64-20-20131211.1-sda-ping.qcow2',
54 'physical_network' : None,
55 'network_type' : None,
56 'segmentation_id' : None
57 }
58
59
60 def get_cal_account():
61 """
62 Creates an object for class RwcalYang.CloudAccount()
63 """
64 account = RwcalYang.CloudAccount()
65 account.name = "Gruntxx"
66 account.account_type = "openstack"
67 account.openstack.key = openstack_info['username']
68 account.openstack.secret = openstack_info['password']
69 account.openstack.auth_url = openstack_info['auth_url']
70 account.openstack.tenant = openstack_info['project_name']
71 account.openstack.mgmt_network = openstack_info['mgmt_network']
72 return account
73
74 def get_cal_plugin():
75 """
76 Loads rw.cal plugin via libpeas
77 """
78 plugin = rw_peas.PeasPlugin('rwcal_openstack', 'RwCal-1.0')
79 engine, info, extension = plugin()
80
81 # Get the RwLogger context
82 rwloggerctx = rwlogger.RwLog.Ctx.new("Cal-Log")
83
84 cal = plugin.get_interface("Cloud")
85 try:
86 rc = cal.init(rwloggerctx)
87 assert rc == RwStatus.SUCCESS
88 except:
89 logger.error("ERROR:Cal plugin instantiation failed. Aborting tests")
90 else:
91 logger.info("Openstack Cal plugin successfully instantiated")
92 return cal
93
94
95 class OpenStackTest(unittest.TestCase):
96 NodeID = "123456789012345" # Some random number to test VM tagging
97 MemoryPageSize = "LARGE"
98 CpuPolicy = "DEDICATED"
99 CpuThreadPolicy = "SEPARATE"
100 CpuThreads = 1
101 NumaNodeCount = 2
102 HostTrust = "trusted"
103 PCIPassThroughAlias = "PCI_10G_ALIAS"
104 SEG_ID = openstack_info['segmentation_id']
105
106 def setUp(self):
107 """
108 Assumption:
109 - It is assumed that openstack install has a flavor and image precreated.
110 - Flavor_name: x1.xlarge
111 - Image_name : rwimage
112
113 If these resources are not then this test will fail.
114 """
115 self._acct = get_cal_account()
116 logger.info("Openstack-CAL-Test: setUp")
117 self.cal = get_cal_plugin()
118 logger.info("Openstack-CAL-Test: setUpEND")
119
120 # First check for VM Flavor and Image and get the corresponding IDs
121 rc, rs = self.cal.get_flavor_list(self._acct)
122 self.assertEqual(rc, RwStatus.SUCCESS)
123
124 flavor_list = [ flavor for flavor in rs.flavorinfo_list if flavor.name == openstack_info['reserved_flavor'] ]
125 self.assertNotEqual(len(flavor_list), 0)
126 self._flavor = flavor_list[0]
127
128 rc, rs = self.cal.get_image_list(self._acct)
129 self.assertEqual(rc, RwStatus.SUCCESS)
130
131 image_list = [ image for image in rs.imageinfo_list if image.name == openstack_info['reserved_image'] ]
132 self.assertNotEqual(len(image_list), 0)
133 self._image = image_list[0]
134
135 rc, rs = self.cal.get_network_list(self._acct)
136 self.assertEqual(rc, RwStatus.SUCCESS)
137 networks = [ network for network in rs.networkinfo_list if ((network.network_name == 'rift.cal.unittest.network') or ('rift.cal.virtual_link' in network.network_name) ) ]
138 for network in networks:
139 logger.debug("Openstack-CAL-Test: Deleting old VL %s", network.network_id)
140 self.cal.delete_virtual_link(self._acct, network.network_id)
141
142 def tearDown(self):
143 logger.info("Openstack-CAL-Test: tearDown")
144
145
146 def _md5(fname, blksize=1048576):
147 hash_md5 = hashlib.md5()
148 with open(fname, "rb") as f:
149 for chunk in iter(lambda: f.read(blksize), b""):
150 hash_md5.update(chunk)
151 return hash_md5.hexdigest()
152
153 @unittest.skip("Skipping test_list_flavors")
154 def test_list_flavor(self):
155 """
156 List existing flavors from openstack installation
157 """
158 logger.info("Openstack-CAL-Test: Starting List Flavors Test")
159 rc, rsp = self.cal.get_flavor_list(self._acct)
160 self.assertEqual(rc, RwStatus.SUCCESS)
161 logger.info("Openstack-CAL-Test: Received %d flavors" %(len(rsp.flavorinfo_list)))
162 for flavor in rsp.flavorinfo_list:
163 rc, flv = self.cal.get_flavor(self._acct, flavor.id)
164 self.assertEqual(rc, RwStatus.SUCCESS)
165 self.assertEqual(flavor.id, flv.id)
166
167 @unittest.skip("Skipping test_list_images")
168 def test_list_images(self):
169 """
170 List existing images from openstack installation
171 """
172 logger.info("Openstack-CAL-Test: Starting List Images Test")
173 rc, rsp = self.cal.get_image_list(self._acct)
174 self.assertEqual(rc, RwStatus.SUCCESS)
175 logger.info("Openstack-CAL-Test: Received %d images" %(len(rsp.imageinfo_list)))
176 #for image in rsp.imageinfo_list:
177 # rc, img = self.cal.get_image(self._acct, image.id)
178 # self.assertEqual(rc, RwStatus.SUCCESS)
179 # self.assertEqual(image.id, img.id)
180
181 @unittest.skip("Skipping test_list_vms")
182 def test_list_vms(self):
183 """
184 List existing VMs from openstack installation
185 """
186 logger.info("Openstack-CAL-Test: Starting List VMs Test")
187 rc, rsp = self.cal.get_vm_list(self._acct)
188 self.assertEqual(rc, RwStatus.SUCCESS)
189 logger.info("Openstack-CAL-Test: Received %d VMs" %(len(rsp.vminfo_list)))
190 for vm in rsp.vminfo_list:
191 rc, server = self.cal.get_vm(self._acct, vm.vm_id)
192 self.assertEqual(vm.vm_id, server.vm_id)
193
194 @unittest.skip("Skipping test_list_networks")
195 def test_list_networks(self):
196 """
197 List existing Network from openstack installation
198 """
199 logger.info("Openstack-CAL-Test: Starting List Networks Test")
200 rc, rsp = self.cal.get_network_list(self._acct)
201 self.assertEqual(rc, RwStatus.SUCCESS)
202 logger.info("Openstack-CAL-Test: Received %d Networks" %(len(rsp.networkinfo_list)))
203 for network in rsp.networkinfo_list:
204 rc, net = self.cal.get_network(self._acct, network.network_id)
205 self.assertEqual(network.network_id, net.network_id)
206
207 @unittest.skip("Skipping test_list_ports")
208 def test_list_ports(self):
209 """
210 List existing Ports from openstack installation
211 """
212 logger.info("Openstack-CAL-Test: Starting List Ports Test")
213 rc, rsp = self.cal.get_port_list(self._acct)
214 self.assertEqual(rc, RwStatus.SUCCESS)
215 assert(rc == RwStatus.SUCCESS)
216 logger.info("Openstack-CAL-Test: Received %d Ports" %(len(rsp.portinfo_list)))
217 for port in rsp.portinfo_list:
218 rc, p = self.cal.get_port(self._acct, port.port_id)
219 self.assertEqual(port.port_id, p.port_id)
220
221 def _get_image_info_request(self):
222 """
223 Returns request object of type RwcalYang.ImageInfoItem()
224 """
225 img = RwcalYang.ImageInfoItem()
226 img.name = "rift.cal.unittest.image"
227 img.location = '/net/sharedfiles/home1/common/vm/rift-root-latest.qcow2'
228 img.disk_format = "qcow2"
229 img.container_format = "bare"
230 img.checksum = self._md5(img.location)
231 return img
232
233 def _get_image_info(self, img_id):
234 """
235 Checks the image status until it becomes active or timeout occurs (100sec)
236 Returns the image_info dictionary
237 """
238 rs = None
239 rc = None
240 for i in range(100):
241 rc, rs = self.cal.get_image(self._acct, img_id)
242 self.assertEqual(rc, RwStatus.SUCCESS)
243 logger.info("Openstack-CAL-Test: Image (image_id: %s) reached state : %s" %(img_id, rs.state))
244 if rs.state == 'active':
245 break
246 else:
247 time.sleep(2) # Sleep for a second
248 return rs
249
250 @unittest.skip("Skipping test_create_delete_image")
251 def test_create_delete_image(self):
252 """
253 Create/Query/Delete a new image in openstack installation
254 """
255 logger.info("Openstack-CAL-Test: Starting Image create test")
256 img = self._get_image_info_request()
257 rc, img_id = self.cal.create_image(self._acct, img)
258 logger.info("Openstack-CAL-Test: Created Image with image_id: %s" %(img_id))
259 self.assertEqual(rc, RwStatus.SUCCESS)
260 img_info = self._get_image_info(img_id)
261 self.assertNotEqual(img_info, None)
262 self.assertEqual(img_id, img_info.id)
263 logger.info("Openstack-CAL-Test: Image (image_id: %s) reached state : %s" %(img_id, img_info.state))
264 self.assertEqual(img_info.has_field('checksum'), True)
265 #self.assertEqual(img_info.checksum, OpenStackTest.IMG_Checksum)
266 logger.info("Openstack-CAL-Test: Initiating Delete Image operation for image_id: %s" %(img_id))
267 rc = self.cal.delete_image(self._acct, img_id)
268 self.assertEqual(rc, RwStatus.SUCCESS)
269 logger.info("Openstack-CAL-Test: Image (image_id: %s) successfully deleted" %(img_id))
270
271 def _get_flavor_info_request(self):
272 """
273 Returns request object of type RwcalYang.FlavorInfoItem()
274 """
275 flavor = RwcalYang.FlavorInfoItem()
276 flavor.name = 'rift.cal.unittest.flavor'
277 flavor.vm_flavor.memory_mb = 16384 # 16GB
278 flavor.vm_flavor.vcpu_count = 4
279 flavor.vm_flavor.storage_gb = 40 # 40GB
280 flavor.guest_epa.mempage_size = OpenStackTest.MemoryPageSize
281 flavor.guest_epa.cpu_pinning_policy = OpenStackTest.CpuPolicy
282 flavor.guest_epa.cpu_thread_pinning_policy = OpenStackTest.CpuThreadPolicy
283 flavor.guest_epa.numa_node_policy.node_cnt = OpenStackTest.NumaNodeCount
284 for i in range(OpenStackTest.NumaNodeCount):
285 node = flavor.guest_epa.numa_node_policy.node.add()
286 node.id = i
287 if i == 0:
288 vcpu = node.vcpu.add()
289 vcpu.id = 0
290 vcpu = node.vcpu.add()
291 vcpu.id = 1
292 elif i == 1:
293 vcpu = node.vcpu.add()
294 vcpu.id = 2
295 vcpu = node.vcpu.add()
296 vcpu.id = 3
297 node.memory_mb = 8196
298 dev = flavor.guest_epa.pcie_device.add()
299 dev.device_id = OpenStackTest.PCIPassThroughAlias
300 dev.count = 1
301 return flavor
302
303 @unittest.skip("Skipping test_create_delete_flavor")
304 def test_create_delete_flavor(self):
305 """
306 Create/Query/Delete a new flavor in openstack installation
307 """
308 logger.info("Openstack-CAL-Test: Starting Image create/delete test")
309
310 ### Delete any previously created flavor with name rift.cal.unittest.flavor
311 rc, rs = self.cal.get_flavor_list(self._acct)
312 self.assertEqual(rc, RwStatus.SUCCESS)
313 flavor_list = [ flavor for flavor in rs.flavorinfo_list if flavor.name == 'rift.cal.unittest.flavor' ]
314 if flavor_list:
315 rc = self.cal.delete_flavor(self._acct, flavor_list[0].id)
316 self.assertEqual(rc, RwStatus.SUCCESS)
317
318 flavor = self._get_flavor_info_request()
319 rc, flavor_id = self.cal.create_flavor(self._acct, flavor)
320 self.assertEqual(rc, RwStatus.SUCCESS)
321
322 logger.info("Openstack-CAL-Test: Created new flavor with flavor_id : %s" %(flavor_id))
323 rc, rs = self.cal.get_flavor(self._acct, flavor_id)
324 self.assertEqual(rc, RwStatus.SUCCESS)
325 self.assertEqual(rs.id, flavor_id)
326
327 # Verify EPA Attributes
328 self.assertEqual(rs.guest_epa.mempage_size, OpenStackTest.MemoryPageSize)
329 self.assertEqual(rs.guest_epa.cpu_pinning_policy, OpenStackTest.CpuPolicy)
330 self.assertEqual(rs.guest_epa.cpu_thread_pinning_policy, OpenStackTest.CpuThreadPolicy)
331 self.assertEqual(rs.guest_epa.numa_node_policy.node_cnt, OpenStackTest.NumaNodeCount)
332 self.assertEqual(len(rs.guest_epa.pcie_device), 1)
333 self.assertEqual(rs.guest_epa.pcie_device[0].device_id, OpenStackTest.PCIPassThroughAlias)
334 self.assertEqual(rs.guest_epa.pcie_device[0].count, 1)
335 logger.info("Openstack-CAL-Test: Initiating delete for flavor_id : %s" %(flavor_id))
336 rc = self.cal.delete_flavor(self._acct, flavor_id)
337 self.assertEqual(rc, RwStatus.SUCCESS)
338 # Check that flavor does not exist anymore in list_flavor
339 rc, rs = self.cal.get_flavor_list(self._acct)
340 self.assertEqual(rc, RwStatus.SUCCESS)
341 flavor_list = [ flavor for flavor in rs.flavorinfo_list if flavor.id == flavor_id ]
342 # Flavor List should be empty
343 self.assertEqual(len(flavor_list), 0)
344 logger.info("Openstack-CAL-Test: Flavor (flavor_id: %s) successfully deleted" %(flavor_id))
345
346 def _get_vm_info_request(self, flavor_id, image_id):
347 """
348 Returns request object of type RwcalYang.VMInfoItem
349 """
350 vm = RwcalYang.VMInfoItem()
351 vm.vm_name = 'rift.cal.unittest.vm'
352 vm.flavor_id = flavor_id
353 vm.image_id = image_id
354 vm.cloud_init.userdata = ''
355 vm.user_tags.node_id = OpenStackTest.NodeID
356 return vm
357
358 def _check_vm_state(self, vm_id, expected_state):
359 """
360 Wait until VM reaches particular state (expected_state).
361 """
362 # Wait while VM goes to required state
363
364 for i in range(50): # 50 poll iterations...
365 rc, rs = self.cal.get_vm(self._acct, vm_id)
366 self.assertEqual(rc, RwStatus.SUCCESS)
367 logger.info("Openstack-CAL-Test: VM vm_id : %s. Current VM state : %s " %(vm_id, rs.state))
368 if rs.state == expected_state:
369 break
370 else:
371 time.sleep(1)
372
373 rc, rs = self.cal.get_vm(self._acct, vm_id)
374 self.assertEqual(rc, RwStatus.SUCCESS)
375 self.assertEqual(rs.state, expected_state)
376
377 def _create_vm(self, flavor, image, port_list = None):
378 """
379 Create VM and perform validity checks
380 """
381 logger.info("Openstack-CAL-Test: Using image : %s and flavor : %s " %(image.name, flavor.name))
382 vm = self._get_vm_info_request(flavor.id, image.id)
383
384 if port_list:
385 for port_id in port_list:
386 port = vm.port_list.add()
387 port.port_id = port_id
388
389 rc, vm_id = self.cal.create_vm(self._acct, vm)
390 self.assertEqual(rc, RwStatus.SUCCESS)
391
392 ### Check if VM creation is successful
393 rc, rs = self.cal.get_vm(self._acct, vm_id)
394 self.assertEqual(rc, RwStatus.SUCCESS)
395 logger.info("Openstack-CAL-Test: Successfully created VM with vm_id : %s. Current VM state : %s " %(vm_id, rs.state))
396
397 ### Ensure the VM state is active
398 self._check_vm_state(vm_id, 'ACTIVE')
399
400 ### Ensure that userdata tags are set as expected
401 rc, rs = self.cal.get_vm(self._acct, vm_id)
402 self.assertEqual(rc, RwStatus.SUCCESS)
403 self.assertEqual(rs.user_tags.has_field('node_id'), True)
404 self.assertEqual(getattr(rs.user_tags, 'node_id'), OpenStackTest.NodeID)
405 logger.info("Openstack-CAL-Test: Successfully verified the user tags for VM-ID: %s" %(vm_id))
406 return rs, vm_id
407
408 def _delete_vm(self, vm_id):
409 """
410 Delete VM and perform validity checks
411 """
412 rc, rs = self.cal.get_vm(self._acct, vm_id)
413 self.assertEqual(rc, RwStatus.SUCCESS)
414
415 logger.info("Openstack-CAL-Test: Initiating VM Delete operation on VM vm_id : %s. Current VM state : %s " %(vm_id, rs.state))
416
417 rc = self.cal.delete_vm(self._acct, vm_id)
418 self.assertEqual(rc, RwStatus.SUCCESS)
419
420 for i in range(50):
421 # Check if VM still exists
422 rc, rs = self.cal.get_vm_list(self._acct)
423 self.assertEqual(rc, RwStatus.SUCCESS)
424 vm_list = [vm for vm in rs.vminfo_list if vm.vm_id == vm_id]
425 if not len(vm_list):
426 break
427
428 rc, rs = self.cal.get_vm_list(self._acct)
429 self.assertEqual(rc, RwStatus.SUCCESS)
430 vm_list = [vm for vm in rs.vminfo_list if vm.vm_id == vm_id]
431 self.assertEqual(len(vm_list), 0)
432 logger.info("Openstack-CAL-Test: VM with vm_id : %s successfully deleted" %(vm_id))
433
434 def _stop_vm(self, vm_id):
435 """
436 Stop VM and perform validity checks
437 """
438 rc, rs = self.cal.get_vm(self._acct, vm_id)
439 self.assertEqual(rc, RwStatus.SUCCESS)
440 logger.info("Openstack-CAL-Test: Initiating Stop VM operation on VM vm_id : %s. Current VM state : %s " %(vm_id, rs.state))
441 rc = self.cal.stop_vm(self._acct, vm_id)
442 self.assertEqual(rc, RwStatus.SUCCESS)
443 ### Ensure that VM state is SHUTOFF
444 self._check_vm_state(vm_id, 'SHUTOFF')
445
446
447 def _start_vm(self, vm_id):
448 """
449 Starts VM and performs validity checks
450 """
451 rc, rs = self.cal.get_vm(self._acct, vm_id)
452 self.assertEqual(rc, RwStatus.SUCCESS)
453 logger.info("Openstack-CAL-Test: Initiating Start VM operation on VM vm_id : %s. Current VM state : %s " %(vm_id, rs.state))
454 rc = self.cal.start_vm(self._acct, vm_id)
455 self.assertEqual(rc, RwStatus.SUCCESS)
456
457 ### Ensure that VM state is ACTIVE
458 self._check_vm_state(vm_id, 'ACTIVE')
459
460
461 def _reboot_vm(self, vm_id):
462 """
463 Reboot VM and perform validity checks
464 """
465 rc, rs = self.cal.get_vm(self._acct, vm_id)
466 self.assertEqual(rc, RwStatus.SUCCESS)
467 logger.info("Openstack-CAL-Test: Initiating Reboot VM operation on VM vm_id : %s. Current VM state : %s " %(vm_id, rs.state))
468 rc = self.cal.reboot_vm(self._acct, vm_id)
469 self.assertEqual(rc, RwStatus.SUCCESS)
470
471 ### Ensure that VM state is ACTIVE
472 self._check_vm_state(vm_id, 'ACTIVE')
473
474 def assert_vm(self, vm_data, flavor):
475 """Verify the newly created VM for attributes specified in the flavor.
476
477 Args:
478 vm_data (VmData): Instance of the newly created VM
479 flavor (FlavorInfoItem): Config flavor.
480 """
481 vm_config = flavor
482
483 # Page size seems to be 4096, regardless of the page size name.
484 page_lookup = {"large": '4096', "small": '4096'}
485 FIELDS = ["vcpus", "cpu_threads", "memory_page_size", "disk",
486 "numa_node_count", "memory", "pci_passthrough_device_list"]
487
488 for field in FIELDS:
489 if field not in vm_config:
490 continue
491
492 vm_value = getattr(vm_data, field)
493 config_value = getattr(vm_config, field)
494
495 if field == "memory_page_size":
496 config_value = page_lookup[config_value]
497
498 if field == "memory":
499 config_value = int(config_value/1000)
500
501 if field == "pci_passthrough_device_list":
502 config_value = len(config_value)
503 vm_value = len(vm_value)
504
505 self.assertEqual(vm_value, config_value)
506
507 @unittest.skip("Skipping test_vm_epa_attributes")
508 def test_vm_epa_attributes(self):
509 """
510 Primary goal: To create a VM with the specified EPA Attributes
511 Secondary goal: To verify flavor creation/delete
512 """
513
514 logger.info("Openstack-CAL-Test: Starting VM(EPA) create/delete test")
515 flavor = self._get_flavor_info_request()
516
517 rc, flavor_id = self.cal.do_create_flavor(self._acct, flavor)
518 self.assertEqual(rc, RwStatus.SUCCESS)
519 flavor.id = flavor_id
520
521 data, vm_id = self._create_vm(flavor, self._image)
522
523 vm_data = VmData(data.host_name, data.management_ip)
524 self.assert_vm(vm_data, flavor)
525
526 self._delete_vm(vm_id)
527
528 rc = self.cal.do_delete_flavor(self._acct, flavor_id)
529 self.assertEqual(rc, RwStatus.SUCCESS)
530
531 @unittest.skip("Skipping test_expiry_token")
532 def test_expiry_token(self):
533 """
534 Primary goal: To verify if we are refreshing the expired tokens.
535 """
536 logger.info("Openstack-CAL-Test: Starting token refresh test")
537 drv = KeystoneDriver(
538 openstack_info['username'],
539 openstack_info['password'],
540 openstack_info['auth_url'],
541 openstack_info['project_name'])
542 # Get hold of the client instance need for Token Manager
543 client = drv._get_keystone_connection()
544
545 auth_ref = client.auth_ref
546 token = auth_ref['auth_token']
547
548 # Verify if the newly acquired token works.
549 nova = NovaDriver(drv)
550 flavors = nova.flavor_list()
551 self.assertTrue(len(flavors) > 1)
552
553 # Invalidate the token
554 token_manger = ksclient.tokens.TokenManager(client)
555 token_manger.revoke_token(token)
556
557 time.sleep(10)
558
559 unauth_exp = False
560 try:
561 flavors = nova.flavor_list()
562 print (flavors)
563 except nova_exception.AuthorizationFailure:
564 unauth_exp = True
565
566 self.assertTrue(unauth_exp)
567
568 # Explicitly reset the expire time, to test if we acquire a new token
569 now = datetime.datetime.utcnow()
570 time_str = format(now, "%Y-%m-%dT%H:%M:%S.%fZ")
571 drv._get_keystone_connection().auth_ref['expires_at'] = time_str
572
573 flavors = nova.flavor_list()
574 self.assertTrue(len(flavors) > 1)
575
576 @unittest.skip("Skipping test_vm_operations")
577 def test_vm_operations(self):
578 """
579 Primary goal: Create/Query/Delete VM in openstack installation.
580 Secondary goal: VM pause/resume operations on VM.
581
582 """
583 logger.info("Openstack-CAL-Test: Starting VM Operations test")
584
585 # Create VM
586 data, vm_id = self._create_vm(self._flavor, self._image)
587
588 # Stop VM
589 self._stop_vm(vm_id)
590 # Start VM
591 self._start_vm(vm_id)
592
593 vm_data = VmData(data.host_name, data.management_ip)
594 self.assert_vm(vm_data, self._flavor)
595
596 # Reboot VM
597 self._reboot_vm(vm_id)
598 ### Delete the VM
599 self._delete_vm(vm_id)
600
601
602 def _get_network_info_request(self):
603 """
604 Returns request object of type RwcalYang.NetworkInfoItem
605 """
606 network = RwcalYang.NetworkInfoItem()
607 network.network_name = 'rift.cal.unittest.network'
608 network.subnet = '192.168.16.0/24'
609 if openstack_info['physical_network']:
610 network.provider_network.physical_network = openstack_info['physical_network']
611 if openstack_info['network_type']:
612 network.provider_network.overlay_type = openstack_info['network_type']
613 if OpenStackTest.SEG_ID:
614 network.provider_network.segmentation_id = OpenStackTest.SEG_ID
615 OpenStackTest.SEG_ID += 1
616 return network
617
618
619 def _create_network(self):
620 """
621 Create a network and verify that network creation is successful
622 """
623 network = self._get_network_info_request()
624
625 ### Create network
626 logger.info("Openstack-CAL-Test: Creating a network with name : %s" %(network.network_name))
627 rc, net_id = self.cal.create_network(self._acct, network)
628 self.assertEqual(rc, RwStatus.SUCCESS)
629
630 ### Verify network is created successfully
631 rc, rs = self.cal.get_network(self._acct, net_id)
632 self.assertEqual(rc, RwStatus.SUCCESS)
633 logger.info("Openstack-CAL-Test: Successfully create Network : %s with id : %s." %(network.network_name, net_id ))
634
635 return net_id
636
637 def _delete_network(self, net_id):
638 """
639 Delete network and verify that delete operation is successful
640 """
641 rc, rs = self.cal.get_network(self._acct, net_id)
642 self.assertEqual(rc, RwStatus.SUCCESS)
643
644 logger.info("Openstack-CAL-Test: Deleting a network with id : %s. " %(net_id))
645 rc = self.cal.delete_network(self._acct, net_id)
646 self.assertEqual(rc, RwStatus.SUCCESS)
647
648 # Verify that network is no longer available via get_network_list API
649 rc, rs = self.cal.get_network_list(self._acct)
650 self.assertEqual(rc, RwStatus.SUCCESS)
651 network_info = [ network for network in rs.networkinfo_list if network.network_id == net_id ]
652 self.assertEqual(len(network_info), 0)
653 logger.info("Openstack-CAL-Test: Successfully deleted Network with id : %s" %(net_id))
654
655
656 @unittest.skip("Skipping test_network_operations")
657 def test_network_operations(self):
658 """
659 Create/Delete Networks
660 """
661 logger.info("Openstack-CAL-Test: Starting Network Operation test")
662
663 ### Create Network
664 net_id = self._create_network()
665
666 ### Delete Network
667 self._delete_network(net_id)
668
669 def _get_port_info_request(self, network_id, vm_id):
670 """
671 Returns an object of type RwcalYang.PortInfoItem
672 """
673 port = RwcalYang.PortInfoItem()
674 port.port_name = 'rift.cal.unittest.port'
675 port.network_id = network_id
676 if vm_id != None:
677 port.vm_id = vm_id
678 return port
679
680 def _create_port(self, net_id, vm_id = None):
681 """
682 Create a port in network with network_id: net_id and verifies that operation is successful
683 """
684 if vm_id != None:
685 logger.info("Openstack-CAL-Test: Creating a port in network with network_id: %s and VM with vm_id: %s" %(net_id, vm_id))
686 else:
687 logger.info("Openstack-CAL-Test: Creating a port in network with network_id: %s" %(net_id))
688
689 ### Create Port
690 port = self._get_port_info_request(net_id, vm_id)
691 rc, port_id = self.cal.create_port(self._acct, port)
692 self.assertEqual(rc, RwStatus.SUCCESS)
693
694 ### Get Port
695 rc, rs = self.cal.get_port(self._acct, port_id)
696 self.assertEqual(rc, RwStatus.SUCCESS)
697 logger.info("Openstack-CAL-Test: Successfully create Port with id : %s. Port State : %s" %(port_id, rs.port_state))
698
699 return port_id
700
701 def _delete_port(self, port_id):
702 """
703 Deletes a port and verifies that operation is successful
704 """
705 rc, rs = self.cal.get_port(self._acct, port_id)
706 self.assertEqual(rc, RwStatus.SUCCESS)
707 logger.info("Openstack-CAL-Test: Deleting Port with id : %s. Port State : %s" %(port_id, rs.port_state))
708
709 ### Delete Port
710 self.cal.delete_port(self._acct, port_id)
711
712 rc, rs = self.cal.get_port_list(self._acct)
713 self.assertEqual(rc, RwStatus.SUCCESS)
714 port_list = [ port for port in rs.portinfo_list if port.port_id == port_id ]
715 self.assertEqual(len(port_list), 0)
716 logger.info("Openstack-CAL-Test: Successfully Deleted Port with id : %s" %(port_id))
717
718 def _monitor_port(self, port_id, expected_state):
719 """
720 Monitor the port state until it reaches expected_state
721 """
722 for i in range(50):
723 rc, rs = self.cal.get_port(self._acct, port_id)
724 self.assertEqual(rc, RwStatus.SUCCESS)
725 logger.info("Openstack-CAL-Test: Port with id : %s. Port State : %s" %(port_id, rs.port_state))
726 if rs.port_state == expected_state:
727 break
728 rc, rs = self.cal.get_port(self._acct, port_id)
729 self.assertEqual(rc, RwStatus.SUCCESS)
730 self.assertEqual(rs.port_state, expected_state)
731 logger.info("Openstack-CAL-Test: Port with port_id : %s reached expected state : %s" %(port_id, rs.port_state))
732
733 @unittest.skip("Skipping test_port_operations_with_vm")
734 def test_port_operations_with_vm(self):
735 """
736 Create/Delete Ports in a network and associate it with a VM
737 """
738 logger.info("Openstack-CAL-Test: Starting Port Operation test with VM")
739
740 ### First create a network
741 net_id = self._create_network()
742
743 ### Create a VM
744 data, vm_id = self._create_vm(self._flavor, self._image)
745
746 ### Now create Port which connects VM to Network
747 port_id = self._create_port(net_id, vm_id)
748
749 ### Verify that port goes to active state
750 self._monitor_port(port_id, 'ACTIVE')
751
752 ### Delete VM
753 self._delete_vm(vm_id)
754
755 ### Delete Port
756 self._delete_port(port_id)
757
758 ### Delete the network
759 self._delete_network(net_id)
760
761 @unittest.skip("Skipping test_create_vm_with_port")
762 def test_create_vm_with_port(self):
763 """
764 Create VM and add ports to it during boot time.
765 """
766 logger.info("Openstack-CAL-Test: Starting Create VM with port test")
767
768 ### First create a network
769 net_id = self._create_network()
770
771 ### Now create Port which connects VM to Network
772 port_id = self._create_port(net_id)
773
774 ### Create a VM
775 data, vm_id = self._create_vm(self._flavor, self._image, [port_id])
776
777 ### Verify that port goes to active state
778 self._monitor_port(port_id, 'ACTIVE')
779
780 ### Delete VM
781 self._delete_vm(vm_id)
782
783 ### Delete Port
784 self._delete_port(port_id)
785
786 ### Delete the network
787 self._delete_network(net_id)
788
789 @unittest.skip("Skipping test_get_vdu_list")
790 def test_get_vdu_list(self):
791 """
792 Test the get_vdu_list API
793 """
794 logger.info("Openstack-CAL-Test: Test Get VDU List APIs")
795 rc, rsp = self.cal.get_vdu_list(self._acct)
796 self.assertEqual(rc, RwStatus.SUCCESS)
797 logger.info("Openstack-CAL-Test: Received %d VDUs" %(len(rsp.vdu_info_list)))
798 for vdu in rsp.vdu_info_list:
799 rc, vdu2 = self.cal.get_vdu(self._acct, vdu.vdu_id)
800 self.assertEqual(vdu2.vdu_id, vdu.vdu_id)
801
802
803 @unittest.skip("Skipping test_get_virtual_link_list")
804 def test_get_virtual_link_list(self):
805 """
806 Test the get_virtual_link_list API
807 """
808 logger.info("Openstack-CAL-Test: Test Get virtual_link List APIs")
809 rc, rsp = self.cal.get_virtual_link_list(self._acct)
810 self.assertEqual(rc, RwStatus.SUCCESS)
811 logger.info("Openstack-CAL-Test: Received %d virtual_links" %(len(rsp.virtual_link_info_list)))
812 for virtual_link in rsp.virtual_link_info_list:
813 rc, virtual_link2 = self.cal.get_virtual_link(self._acct, virtual_link.virtual_link_id)
814 self.assertEqual(virtual_link2.virtual_link_id, virtual_link.virtual_link_id)
815
816 def _get_virtual_link_request_info(self):
817 """
818 Returns object of type RwcalYang.VirtualLinkReqParams
819 """
820 vlink = RwcalYang.VirtualLinkReqParams()
821 vlink.name = 'rift.cal.virtual_link'
822 vlink.subnet = '192.168.1.0/24'
823 if openstack_info['physical_network']:
824 vlink.provider_network.physical_network = openstack_info['physical_network']
825 if openstack_info['network_type']:
826 vlink.provider_network.overlay_type = openstack_info['network_type'].upper()
827 if OpenStackTest.SEG_ID:
828 vlink.provider_network.segmentation_id = OpenStackTest.SEG_ID
829 OpenStackTest.SEG_ID += 1
830 return vlink
831
832 def _get_vdu_request_info(self, virtual_link_id):
833 """
834 Returns object of type RwcalYang.VDUInitParams
835 """
836 vdu = RwcalYang.VDUInitParams()
837 vdu.name = "cal.vdu"
838 vdu.node_id = OpenStackTest.NodeID
839 vdu.image_id = self._image.id
840 vdu.flavor_id = self._flavor.id
841 vdu.vdu_init.userdata = PING_USERDATA
842 vdu.allocate_public_address = True
843 meta1 = vdu.custom_boot_data.custom_meta_data.add()
844 meta1.name = "EMS_IP"
845 meta1.data_type = "STRING"
846 meta1.value = "10.5.6.6"
847 #meta2 = vdu.custom_boot_data.custom_meta_data.add()
848 #meta2.name = "Cluster_data"
849 #meta2.data_type = "JSON"
850 #meta2.value = '''{ "cluster_id": "12" , "vnfc_id": "112" }'''
851 #vdu.custom_boot_data.custom_drive = True
852 customfile1 = vdu.custom_boot_data.custom_config_files.add()
853 customfile1.source = "abcdef124"
854 customfile1.dest = "/tmp/tempfile.txt"
855 customfile2 = vdu.custom_boot_data.custom_config_files.add()
856 customfile2.source = "123456"
857 customfile2.dest = "/tmp/tempfile2.txt"
858 c1 = vdu.connection_points.add()
859 c1.name = "c_point1"
860 c1.virtual_link_id = virtual_link_id
861 c1.type_yang = 'VIRTIO'
862 return vdu
863
864 def _get_vdu_modify_request_info(self, vdu_id, virtual_link_id):
865 """
866 Returns object of type RwcalYang.VDUModifyParams
867 """
868 vdu = RwcalYang.VDUModifyParams()
869 vdu.vdu_id = vdu_id
870 c1 = vdu.connection_points_add.add()
871 c1.name = "c_modify1"
872 c1.virtual_link_id = virtual_link_id
873
874 return vdu
875
876 #@unittest.skip("Skipping test_create_delete_virtual_link_and_vdu")
877 def test_create_delete_virtual_link_and_vdu(self):
878 """
879 Test to create VDU
880 """
881 logger.info("Openstack-CAL-Test: Test Create Virtual Link API")
882 vlink_req = self._get_virtual_link_request_info()
883
884 rc, rsp = self.cal.create_virtual_link(self._acct, vlink_req)
885 self.assertEqual(rc.status, RwStatus.SUCCESS)
886 logger.info("Openstack-CAL-Test: Created virtual_link with Id: %s" %rsp)
887 vlink_id = rsp
888
889 #Check if virtual_link create is successful
890 rc, rsp = self.cal.get_virtual_link(self._acct, rsp)
891 self.assertEqual(rc, RwStatus.SUCCESS)
892 self.assertEqual(rsp.virtual_link_id, vlink_id)
893
894 # Now create VDU
895 vdu_req = self._get_vdu_request_info(vlink_id)
896 logger.info("Openstack-CAL-Test: Test Create VDU API")
897
898 rc, rsp = self.cal.create_vdu(self._acct, vdu_req)
899 self.assertEqual(rc.status, RwStatus.SUCCESS)
900 logger.info("Openstack-CAL-Test: Created vdu with Id: %s" %rsp)
901
902 vdu_id = rsp
903
904 ## Check if VDU create is successful
905 rc, rsp = self.cal.get_vdu(self._acct, rsp)
906 self.assertEqual(rsp.vdu_id, vdu_id)
907
908 ### Wait until vdu_state is active
909 for i in range(50):
910 rc, rs = self.cal.get_vdu(self._acct, vdu_id)
911 self.assertEqual(rc, RwStatus.SUCCESS)
912 logger.info("Openstack-CAL-Test: VDU with id : %s. Reached State : %s" %(vdu_id, rs.state))
913 if rs.state == 'active':
914 break
915 rc, rs = self.cal.get_vdu(self._acct, vdu_id)
916 self.assertEqual(rc, RwStatus.SUCCESS)
917 self.assertEqual(rs.state, 'active')
918 logger.info("Openstack-CAL-Test: VDU with id : %s reached expected state : %s" %(vdu_id, rs.state))
919 logger.info("Openstack-CAL-Test: VDUInfo: %s" %(rs))
920
921 vlink_req = self._get_virtual_link_request_info()
922
923 ### Create another virtual_link
924 rc, rsp = self.cal.create_virtual_link(self._acct, vlink_req)
925 self.assertEqual(rc.status, RwStatus.SUCCESS)
926 logger.info("Openstack-CAL-Test: Created virtual_link with Id: %s" %rsp)
927 vlink_id2= rsp
928
929 ### Now exercise the modify_vdu_api
930 vdu_modify = self._get_vdu_modify_request_info(vdu_id, vlink_id2)
931 rc = self.cal.modify_vdu(self._acct, vdu_modify)
932 self.assertEqual(rc, RwStatus.SUCCESS)
933 logger.info("Openstack-CAL-Test: Modified vdu with Id: %s" %vdu_id)
934
935 ### Lets delete the VDU
936 self.cal.delete_vdu(self._acct, vdu_id)
937
938 ### Lets delete the Virtual Link
939 self.cal.delete_virtual_link(self._acct, vlink_id)
940
941 ### Lets delete the Virtual Link-2
942 self.cal.delete_virtual_link(self._acct, vlink_id2)
943
944 time.sleep(5)
945 ### Verify that VDU and virtual link are successfully deleted
946 rc, rsp = self.cal.get_vdu_list(self._acct)
947 self.assertEqual(rc, RwStatus.SUCCESS)
948 for vdu in rsp.vdu_info_list:
949 self.assertNotEqual(vdu.vdu_id, vdu_id)
950
951 rc, rsp = self.cal.get_virtual_link_list(self._acct)
952 self.assertEqual(rc, RwStatus.SUCCESS)
953
954 for virtual_link in rsp.virtual_link_info_list:
955 self.assertNotEqual(virtual_link.virtual_link_id, vlink_id)
956
957 logger.info("Openstack-CAL-Test: VDU/Virtual Link create-delete test successfully completed")
958
959 class VmData(object):
960 """A convenience class that provides all the stats and EPA Attributes
961 from the VM provided
962 """
963 def __init__(self, host, mgmt_ip):
964 """
965 Args:
966 host (str): host name.
967 mgmt_ip (str): The IP of the newly created VM.
968 """
969 # Sleep for 20s to ensure the VM is UP and ready to run commands
970 time.sleep(20)
971 logger.info("Connecting to host: {} and IP: {}".format(host, mgmt_ip))
972 self.client = paramiko.SSHClient()
973 self.client.set_missing_host_key_policy(paramiko.WarningPolicy())
974 self.client.connect(host)
975 self.ip = mgmt_ip
976
977 # Get all data from the newly created VM.
978 self._data = self._get_data()
979 self._page_size = self._exec_and_clean("getconf PAGE_SIZE")
980 self._disk_space = self._exec_and_clean(
981 "df -kh --output=size /",
982 line_no=1)
983 self._pci_data = self._exec('lspci -m | grep "10-Gigabit"')
984
985 def _get_data(self,):
986 """Runs the command and store the output in a python dict.
987
988 Returns:
989 dict: Containing all key => value pairs.
990 """
991 content = {}
992 cmds = ["lscpu", 'less /proc/meminfo']
993 for cmd in cmds:
994 ssh_out = self._exec(cmd)
995 content.update(self._convert_to_dict(ssh_out))
996 return content
997
998 def _exec_and_clean(self, cmd, line_no=0):
999 """A convenience method to run a command and extract the specified line
1000 number.
1001
1002 Args:
1003 cmd (str): Command to execute
1004 line_no (int, optional): Default to 0, extracts the first line.
1005
1006 Returns:
1007 str: line_no of the output of the command.
1008 """
1009 output = self._exec(cmd)[line_no]
1010 output = ' '.join(output.split())
1011 return output.strip()
1012
1013 def _exec(self, cmd):
1014 """Thin wrapper that runs the command and returns the stdout data
1015
1016 Args:
1017 cmd (str): Command to execute.
1018
1019 Returns:
1020 list: Contains the command output.
1021 """
1022 _, ssh_out, _ = self.client.exec_command(
1023 "/usr/rift/bin/ssh_root {} {}".format(self.ip,
1024 cmd))
1025 return ssh_out.readlines()
1026
1027 def _convert_to_dict(self, content):
1028 """convenience method that cleans and stores the line into dict.
1029 data is split based on ":" or " ".
1030
1031 Args:
1032 content (list): A list containing the stdout.
1033
1034 Returns:
1035 dict: containing stat attribute => value.
1036 """
1037 flattened = {}
1038 for line in content:
1039 line = ' '.join(line.split())
1040 if ":" in line:
1041 key, value = line.split(":")
1042 else:
1043 key, value = line.split(" ")
1044 key, value = key.strip(), value.strip()
1045 flattened[key] = value
1046 return flattened
1047
1048 @property
1049 def disk(self):
1050 disk = self._disk_space.replace("G", "")
1051 return int(disk)
1052
1053 @property
1054 def numa_node_count(self):
1055 numa_cores = self._data['NUMA node(s)']
1056 numa_cores = int(numa_cores)
1057 return numa_cores
1058
1059 @property
1060 def vcpus(self):
1061 cores = int(self._data['CPU(s)'])
1062 return cores
1063
1064 @property
1065 def cpu_threads(self):
1066 threads = int(self._data['Thread(s) per core'])
1067 return threads
1068
1069 @property
1070 def memory(self):
1071 memory = self._data['MemTotal']
1072 memory = int(memory.replace("kB", ""))/1000/1000
1073 return int(memory)
1074
1075 @property
1076 def memory_page_size(self):
1077 return self._page_size
1078
1079 @property
1080 def pci_passthrough_device_list(self):
1081 return self._pci_data
1082
1083
1084 if __name__ == "__main__":
1085 logging.basicConfig(level=logging.DEBUG)
1086 unittest.main()