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