RIFT OSM R1 Initial Submission
[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 node.vcpu = [0,1]
280 elif i == 1:
281 node.vcpu = [2,3]
282 node.memory_mb = 8196
283 dev = flavor.guest_epa.pcie_device.add()
284 dev.device_id = OpenStackTest.PCIPassThroughAlias
285 dev.count = 1
286 return flavor
287
288 @unittest.skip("Skipping test_create_delete_flavor")
289 def test_create_delete_flavor(self):
290 """
291 Create/Query/Delete a new flavor in openstack installation
292 """
293 logger.info("Openstack-CAL-Test: Starting Image create/delete test")
294
295 ### Delete any previously created flavor with name rift.cal.unittest.flavor
296 rc, rs = self.cal.get_flavor_list(self._acct)
297 self.assertEqual(rc, RwStatus.SUCCESS)
298 flavor_list = [ flavor for flavor in rs.flavorinfo_list if flavor.name == 'rift.cal.unittest.flavor' ]
299 if flavor_list:
300 rc = self.cal.delete_flavor(self._acct, flavor_list[0].id)
301 self.assertEqual(rc, RwStatus.SUCCESS)
302
303 flavor = self._get_flavor_info_request()
304 rc, flavor_id = self.cal.create_flavor(self._acct, flavor)
305 self.assertEqual(rc, RwStatus.SUCCESS)
306
307 logger.info("Openstack-CAL-Test: Created new flavor with flavor_id : %s" %(flavor_id))
308 rc, rs = self.cal.get_flavor(self._acct, flavor_id)
309 self.assertEqual(rc, RwStatus.SUCCESS)
310 self.assertEqual(rs.id, flavor_id)
311
312 # Verify EPA Attributes
313 self.assertEqual(rs.guest_epa.mempage_size, OpenStackTest.MemoryPageSize)
314 self.assertEqual(rs.guest_epa.cpu_pinning_policy, OpenStackTest.CpuPolicy)
315 self.assertEqual(rs.guest_epa.cpu_thread_pinning_policy, OpenStackTest.CpuThreadPolicy)
316 self.assertEqual(rs.guest_epa.numa_node_policy.node_cnt, OpenStackTest.NumaNodeCount)
317 self.assertEqual(len(rs.guest_epa.pcie_device), 1)
318 self.assertEqual(rs.guest_epa.pcie_device[0].device_id, OpenStackTest.PCIPassThroughAlias)
319 self.assertEqual(rs.guest_epa.pcie_device[0].count, 1)
320 logger.info("Openstack-CAL-Test: Initiating delete for flavor_id : %s" %(flavor_id))
321 rc = self.cal.delete_flavor(self._acct, flavor_id)
322 self.assertEqual(rc, RwStatus.SUCCESS)
323 # Check that flavor does not exist anymore in list_flavor
324 rc, rs = self.cal.get_flavor_list(self._acct)
325 self.assertEqual(rc, RwStatus.SUCCESS)
326 flavor_list = [ flavor for flavor in rs.flavorinfo_list if flavor.id == flavor_id ]
327 # Flavor List should be empty
328 self.assertEqual(len(flavor_list), 0)
329 logger.info("Openstack-CAL-Test: Flavor (flavor_id: %s) successfully deleted" %(flavor_id))
330
331 def _get_vm_info_request(self, flavor_id, image_id):
332 """
333 Returns request object of type RwcalYang.VMInfoItem
334 """
335 vm = RwcalYang.VMInfoItem()
336 vm.vm_name = 'rift.cal.unittest.vm'
337 vm.flavor_id = flavor_id
338 vm.image_id = image_id
339 vm.cloud_init.userdata = ''
340 vm.user_tags.node_id = OpenStackTest.NodeID
341 return vm
342
343 def _check_vm_state(self, vm_id, expected_state):
344 """
345 Wait until VM reaches particular state (expected_state).
346 """
347 # Wait while VM goes to required state
348
349 for i in range(50): # 50 poll iterations...
350 rc, rs = self.cal.get_vm(self._acct, vm_id)
351 self.assertEqual(rc, RwStatus.SUCCESS)
352 logger.info("Openstack-CAL-Test: VM vm_id : %s. Current VM state : %s " %(vm_id, rs.state))
353 if rs.state == expected_state:
354 break
355 else:
356 time.sleep(1)
357
358 rc, rs = self.cal.get_vm(self._acct, vm_id)
359 self.assertEqual(rc, RwStatus.SUCCESS)
360 self.assertEqual(rs.state, expected_state)
361
362 def _create_vm(self, flavor, image, port_list = None):
363 """
364 Create VM and perform validity checks
365 """
366 logger.info("Openstack-CAL-Test: Using image : %s and flavor : %s " %(image.name, flavor.name))
367 vm = self._get_vm_info_request(flavor.id, image.id)
368
369 if port_list:
370 for port_id in port_list:
371 port = vm.port_list.add()
372 port.port_id = port_id
373
374 rc, vm_id = self.cal.create_vm(self._acct, vm)
375 self.assertEqual(rc, RwStatus.SUCCESS)
376
377 ### Check if VM creation is successful
378 rc, rs = self.cal.get_vm(self._acct, vm_id)
379 self.assertEqual(rc, RwStatus.SUCCESS)
380 logger.info("Openstack-CAL-Test: Successfully created VM with vm_id : %s. Current VM state : %s " %(vm_id, rs.state))
381
382 ### Ensure the VM state is active
383 self._check_vm_state(vm_id, 'ACTIVE')
384
385 ### Ensure that userdata tags are set as expected
386 rc, rs = self.cal.get_vm(self._acct, vm_id)
387 self.assertEqual(rc, RwStatus.SUCCESS)
388 self.assertEqual(rs.user_tags.has_field('node_id'), True)
389 self.assertEqual(getattr(rs.user_tags, 'node_id'), OpenStackTest.NodeID)
390 logger.info("Openstack-CAL-Test: Successfully verified the user tags for VM-ID: %s" %(vm_id))
391 return rs, vm_id
392
393 def _delete_vm(self, vm_id):
394 """
395 Delete VM and perform validity checks
396 """
397 rc, rs = self.cal.get_vm(self._acct, vm_id)
398 self.assertEqual(rc, RwStatus.SUCCESS)
399
400 logger.info("Openstack-CAL-Test: Initiating VM Delete operation on VM vm_id : %s. Current VM state : %s " %(vm_id, rs.state))
401
402 rc = self.cal.delete_vm(self._acct, vm_id)
403 self.assertEqual(rc, RwStatus.SUCCESS)
404
405 for i in range(50):
406 # Check if VM still exists
407 rc, rs = self.cal.get_vm_list(self._acct)
408 self.assertEqual(rc, RwStatus.SUCCESS)
409 vm_list = [vm for vm in rs.vminfo_list if vm.vm_id == vm_id]
410 if not len(vm_list):
411 break
412
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 self.assertEqual(len(vm_list), 0)
417 logger.info("Openstack-CAL-Test: VM with vm_id : %s successfully deleted" %(vm_id))
418
419 def _stop_vm(self, vm_id):
420 """
421 Stop VM and perform validity checks
422 """
423 rc, rs = self.cal.get_vm(self._acct, vm_id)
424 self.assertEqual(rc, RwStatus.SUCCESS)
425 logger.info("Openstack-CAL-Test: Initiating Stop VM operation on VM vm_id : %s. Current VM state : %s " %(vm_id, rs.state))
426 rc = self.cal.stop_vm(self._acct, vm_id)
427 self.assertEqual(rc, RwStatus.SUCCESS)
428 ### Ensure that VM state is SHUTOFF
429 self._check_vm_state(vm_id, 'SHUTOFF')
430
431
432 def _start_vm(self, vm_id):
433 """
434 Starts VM and performs validity checks
435 """
436 rc, rs = self.cal.get_vm(self._acct, vm_id)
437 self.assertEqual(rc, RwStatus.SUCCESS)
438 logger.info("Openstack-CAL-Test: Initiating Start VM operation on VM vm_id : %s. Current VM state : %s " %(vm_id, rs.state))
439 rc = self.cal.start_vm(self._acct, vm_id)
440 self.assertEqual(rc, RwStatus.SUCCESS)
441
442 ### Ensure that VM state is ACTIVE
443 self._check_vm_state(vm_id, 'ACTIVE')
444
445
446 def _reboot_vm(self, vm_id):
447 """
448 Reboot VM and perform validity checks
449 """
450 rc, rs = self.cal.get_vm(self._acct, vm_id)
451 self.assertEqual(rc, RwStatus.SUCCESS)
452 logger.info("Openstack-CAL-Test: Initiating Reboot VM operation on VM vm_id : %s. Current VM state : %s " %(vm_id, rs.state))
453 rc = self.cal.reboot_vm(self._acct, vm_id)
454 self.assertEqual(rc, RwStatus.SUCCESS)
455
456 ### Ensure that VM state is ACTIVE
457 self._check_vm_state(vm_id, 'ACTIVE')
458
459 def assert_vm(self, vm_data, flavor):
460 """Verify the newly created VM for attributes specified in the flavor.
461
462 Args:
463 vm_data (VmData): Instance of the newly created VM
464 flavor (FlavorInfoItem): Config flavor.
465 """
466 vm_config = flavor
467
468 # Page size seems to be 4096, regardless of the page size name.
469 page_lookup = {"large": '4096', "small": '4096'}
470 FIELDS = ["vcpus", "cpu_threads", "memory_page_size", "disk",
471 "numa_node_count", "memory", "pci_passthrough_device_list"]
472
473 for field in FIELDS:
474 if field not in vm_config:
475 continue
476
477 vm_value = getattr(vm_data, field)
478 config_value = getattr(vm_config, field)
479
480 if field == "memory_page_size":
481 config_value = page_lookup[config_value]
482
483 if field == "memory":
484 config_value = int(config_value/1000)
485
486 if field == "pci_passthrough_device_list":
487 config_value = len(config_value)
488 vm_value = len(vm_value)
489
490 self.assertEqual(vm_value, config_value)
491
492 @unittest.skip("Skipping test_vm_epa_attributes")
493 def test_vm_epa_attributes(self):
494 """
495 Primary goal: To create a VM with the specified EPA Attributes
496 Secondary goal: To verify flavor creation/delete
497 """
498
499 logger.info("Openstack-CAL-Test: Starting VM(EPA) create/delete test")
500 flavor = self._get_flavor_info_request()
501
502 rc, flavor_id = self.cal.do_create_flavor(self._acct, flavor)
503 self.assertEqual(rc, RwStatus.SUCCESS)
504 flavor.id = flavor_id
505
506 data, vm_id = self._create_vm(flavor, self._image)
507
508 vm_data = VmData(data.host_name, data.management_ip)
509 self.assert_vm(vm_data, flavor)
510
511 self._delete_vm(vm_id)
512
513 rc = self.cal.do_delete_flavor(self._acct, flavor_id)
514 self.assertEqual(rc, RwStatus.SUCCESS)
515
516 @unittest.skip("Skipping test_expiry_token")
517 def test_expiry_token(self):
518 """
519 Primary goal: To verify if we are refreshing the expired tokens.
520 """
521 logger.info("Openstack-CAL-Test: Starting token refresh test")
522 drv = KeystoneDriver(
523 openstack_info['username'],
524 openstack_info['password'],
525 openstack_info['auth_url'],
526 openstack_info['project_name'])
527 # Get hold of the client instance need for Token Manager
528 client = drv._get_keystone_connection()
529
530 auth_ref = client.auth_ref
531 token = auth_ref['auth_token']
532
533 # Verify if the newly acquired token works.
534 nova = NovaDriver(drv)
535 flavors = nova.flavor_list()
536 self.assertTrue(len(flavors) > 1)
537
538 # Invalidate the token
539 token_manger = ksclient.tokens.TokenManager(client)
540 token_manger.revoke_token(token)
541
542 time.sleep(10)
543
544 unauth_exp = False
545 try:
546 flavors = nova.flavor_list()
547 print (flavors)
548 except nova_exception.AuthorizationFailure:
549 unauth_exp = True
550
551 self.assertTrue(unauth_exp)
552
553 # Explicitly reset the expire time, to test if we acquire a new token
554 now = datetime.datetime.utcnow()
555 time_str = format(now, "%Y-%m-%dT%H:%M:%S.%fZ")
556 drv._get_keystone_connection().auth_ref['expires_at'] = time_str
557
558 flavors = nova.flavor_list()
559 self.assertTrue(len(flavors) > 1)
560
561 @unittest.skip("Skipping test_vm_operations")
562 def test_vm_operations(self):
563 """
564 Primary goal: Create/Query/Delete VM in openstack installation.
565 Secondary goal: VM pause/resume operations on VM.
566
567 """
568 logger.info("Openstack-CAL-Test: Starting VM Operations test")
569
570 # Create VM
571 data, vm_id = self._create_vm(self._flavor, self._image)
572
573 # Stop VM
574 self._stop_vm(vm_id)
575 # Start VM
576 self._start_vm(vm_id)
577
578 vm_data = VmData(data.host_name, data.management_ip)
579 self.assert_vm(vm_data, self._flavor)
580
581 # Reboot VM
582 self._reboot_vm(vm_id)
583 ### Delete the VM
584 self._delete_vm(vm_id)
585
586
587 def _get_network_info_request(self):
588 """
589 Returns request object of type RwcalYang.NetworkInfoItem
590 """
591 network = RwcalYang.NetworkInfoItem()
592 network.network_name = 'rift.cal.unittest.network'
593 network.subnet = '192.168.16.0/24'
594 if openstack_info['physical_network']:
595 network.provider_network.physical_network = openstack_info['physical_network']
596 if openstack_info['network_type']:
597 network.provider_network.overlay_type = openstack_info['network_type']
598 if OpenStackTest.SEG_ID:
599 network.provider_network.segmentation_id = OpenStackTest.SEG_ID
600 OpenStackTest.SEG_ID += 1
601 return network
602
603
604 def _create_network(self):
605 """
606 Create a network and verify that network creation is successful
607 """
608 network = self._get_network_info_request()
609
610 ### Create network
611 logger.info("Openstack-CAL-Test: Creating a network with name : %s" %(network.network_name))
612 rc, net_id = self.cal.create_network(self._acct, network)
613 self.assertEqual(rc, RwStatus.SUCCESS)
614
615 ### Verify network is created successfully
616 rc, rs = self.cal.get_network(self._acct, net_id)
617 self.assertEqual(rc, RwStatus.SUCCESS)
618 logger.info("Openstack-CAL-Test: Successfully create Network : %s with id : %s." %(network.network_name, net_id ))
619
620 return net_id
621
622 def _delete_network(self, net_id):
623 """
624 Delete network and verify that delete operation is successful
625 """
626 rc, rs = self.cal.get_network(self._acct, net_id)
627 self.assertEqual(rc, RwStatus.SUCCESS)
628
629 logger.info("Openstack-CAL-Test: Deleting a network with id : %s. " %(net_id))
630 rc = self.cal.delete_network(self._acct, net_id)
631 self.assertEqual(rc, RwStatus.SUCCESS)
632
633 # Verify that network is no longer available via get_network_list API
634 rc, rs = self.cal.get_network_list(self._acct)
635 self.assertEqual(rc, RwStatus.SUCCESS)
636 network_info = [ network for network in rs.networkinfo_list if network.network_id == net_id ]
637 self.assertEqual(len(network_info), 0)
638 logger.info("Openstack-CAL-Test: Successfully deleted Network with id : %s" %(net_id))
639
640
641 @unittest.skip("Skipping test_network_operations")
642 def test_network_operations(self):
643 """
644 Create/Delete Networks
645 """
646 logger.info("Openstack-CAL-Test: Starting Network Operation test")
647
648 ### Create Network
649 net_id = self._create_network()
650
651 ### Delete Network
652 self._delete_network(net_id)
653
654 def _get_port_info_request(self, network_id, vm_id):
655 """
656 Returns an object of type RwcalYang.PortInfoItem
657 """
658 port = RwcalYang.PortInfoItem()
659 port.port_name = 'rift.cal.unittest.port'
660 port.network_id = network_id
661 if vm_id != None:
662 port.vm_id = vm_id
663 return port
664
665 def _create_port(self, net_id, vm_id = None):
666 """
667 Create a port in network with network_id: net_id and verifies that operation is successful
668 """
669 if vm_id != None:
670 logger.info("Openstack-CAL-Test: Creating a port in network with network_id: %s and VM with vm_id: %s" %(net_id, vm_id))
671 else:
672 logger.info("Openstack-CAL-Test: Creating a port in network with network_id: %s" %(net_id))
673
674 ### Create Port
675 port = self._get_port_info_request(net_id, vm_id)
676 rc, port_id = self.cal.create_port(self._acct, port)
677 self.assertEqual(rc, RwStatus.SUCCESS)
678
679 ### Get Port
680 rc, rs = self.cal.get_port(self._acct, port_id)
681 self.assertEqual(rc, RwStatus.SUCCESS)
682 logger.info("Openstack-CAL-Test: Successfully create Port with id : %s. Port State : %s" %(port_id, rs.port_state))
683
684 return port_id
685
686 def _delete_port(self, port_id):
687 """
688 Deletes a port and verifies that operation is successful
689 """
690 rc, rs = self.cal.get_port(self._acct, port_id)
691 self.assertEqual(rc, RwStatus.SUCCESS)
692 logger.info("Openstack-CAL-Test: Deleting Port with id : %s. Port State : %s" %(port_id, rs.port_state))
693
694 ### Delete Port
695 self.cal.delete_port(self._acct, port_id)
696
697 rc, rs = self.cal.get_port_list(self._acct)
698 self.assertEqual(rc, RwStatus.SUCCESS)
699 port_list = [ port for port in rs.portinfo_list if port.port_id == port_id ]
700 self.assertEqual(len(port_list), 0)
701 logger.info("Openstack-CAL-Test: Successfully Deleted Port with id : %s" %(port_id))
702
703 def _monitor_port(self, port_id, expected_state):
704 """
705 Monitor the port state until it reaches expected_state
706 """
707 for i in range(50):
708 rc, rs = self.cal.get_port(self._acct, port_id)
709 self.assertEqual(rc, RwStatus.SUCCESS)
710 logger.info("Openstack-CAL-Test: Port with id : %s. Port State : %s" %(port_id, rs.port_state))
711 if rs.port_state == expected_state:
712 break
713 rc, rs = self.cal.get_port(self._acct, port_id)
714 self.assertEqual(rc, RwStatus.SUCCESS)
715 self.assertEqual(rs.port_state, expected_state)
716 logger.info("Openstack-CAL-Test: Port with port_id : %s reached expected state : %s" %(port_id, rs.port_state))
717
718 @unittest.skip("Skipping test_port_operations_with_vm")
719 def test_port_operations_with_vm(self):
720 """
721 Create/Delete Ports in a network and associate it with a VM
722 """
723 logger.info("Openstack-CAL-Test: Starting Port Operation test with VM")
724
725 ### First create a network
726 net_id = self._create_network()
727
728 ### Create a VM
729 data, vm_id = self._create_vm(self._flavor, self._image)
730
731 ### Now create Port which connects VM to Network
732 port_id = self._create_port(net_id, vm_id)
733
734 ### Verify that port goes to active state
735 self._monitor_port(port_id, 'ACTIVE')
736
737 ### Delete VM
738 self._delete_vm(vm_id)
739
740 ### Delete Port
741 self._delete_port(port_id)
742
743 ### Delete the network
744 self._delete_network(net_id)
745
746 @unittest.skip("Skipping test_create_vm_with_port")
747 def test_create_vm_with_port(self):
748 """
749 Create VM and add ports to it during boot time.
750 """
751 logger.info("Openstack-CAL-Test: Starting Create VM with port test")
752
753 ### First create a network
754 net_id = self._create_network()
755
756 ### Now create Port which connects VM to Network
757 port_id = self._create_port(net_id)
758
759 ### Create a VM
760 data, vm_id = self._create_vm(self._flavor, self._image, [port_id])
761
762 ### Verify that port goes to active state
763 self._monitor_port(port_id, 'ACTIVE')
764
765 ### Delete VM
766 self._delete_vm(vm_id)
767
768 ### Delete Port
769 self._delete_port(port_id)
770
771 ### Delete the network
772 self._delete_network(net_id)
773
774 @unittest.skip("Skipping test_get_vdu_list")
775 def test_get_vdu_list(self):
776 """
777 Test the get_vdu_list API
778 """
779 logger.info("Openstack-CAL-Test: Test Get VDU List APIs")
780 rc, rsp = self.cal.get_vdu_list(self._acct)
781 self.assertEqual(rc, RwStatus.SUCCESS)
782 logger.info("Openstack-CAL-Test: Received %d VDUs" %(len(rsp.vdu_info_list)))
783 for vdu in rsp.vdu_info_list:
784 rc, vdu2 = self.cal.get_vdu(self._acct, vdu.vdu_id)
785 self.assertEqual(vdu2.vdu_id, vdu.vdu_id)
786
787
788 @unittest.skip("Skipping test_get_virtual_link_list")
789 def test_get_virtual_link_list(self):
790 """
791 Test the get_virtual_link_list API
792 """
793 logger.info("Openstack-CAL-Test: Test Get virtual_link List APIs")
794 rc, rsp = self.cal.get_virtual_link_list(self._acct)
795 self.assertEqual(rc, RwStatus.SUCCESS)
796 logger.info("Openstack-CAL-Test: Received %d virtual_links" %(len(rsp.virtual_link_info_list)))
797 for virtual_link in rsp.virtual_link_info_list:
798 rc, virtual_link2 = self.cal.get_virtual_link(self._acct, virtual_link.virtual_link_id)
799 self.assertEqual(virtual_link2.virtual_link_id, virtual_link.virtual_link_id)
800
801 def _get_virtual_link_request_info(self):
802 """
803 Returns object of type RwcalYang.VirtualLinkReqParams
804 """
805 vlink = RwcalYang.VirtualLinkReqParams()
806 vlink.name = 'rift.cal.virtual_link'
807 vlink.subnet = '192.168.1.0/24'
808 if openstack_info['physical_network']:
809 vlink.provider_network.physical_network = openstack_info['physical_network']
810 if openstack_info['network_type']:
811 vlink.provider_network.overlay_type = openstack_info['network_type'].upper()
812 if OpenStackTest.SEG_ID:
813 vlink.provider_network.segmentation_id = OpenStackTest.SEG_ID
814 OpenStackTest.SEG_ID += 1
815 return vlink
816
817 def _get_vdu_request_info(self, virtual_link_id):
818 """
819 Returns object of type RwcalYang.VDUInitParams
820 """
821 vdu = RwcalYang.VDUInitParams()
822 vdu.name = "cal.vdu"
823 vdu.node_id = OpenStackTest.NodeID
824 vdu.image_id = self._image.id
825 vdu.flavor_id = self._flavor.id
826 vdu.vdu_init.userdata = ''
827 vdu.allocate_public_address = True
828 c1 = vdu.connection_points.add()
829 c1.name = "c_point1"
830 c1.virtual_link_id = virtual_link_id
831 c1.type_yang = 'VIRTIO'
832 return vdu
833
834 def _get_vdu_modify_request_info(self, vdu_id, virtual_link_id):
835 """
836 Returns object of type RwcalYang.VDUModifyParams
837 """
838 vdu = RwcalYang.VDUModifyParams()
839 vdu.vdu_id = vdu_id
840 c1 = vdu.connection_points_add.add()
841 c1.name = "c_modify1"
842 c1.virtual_link_id = virtual_link_id
843
844 return vdu
845
846 #@unittest.skip("Skipping test_create_delete_virtual_link_and_vdu")
847 def test_create_delete_virtual_link_and_vdu(self):
848 """
849 Test to create VDU
850 """
851 logger.info("Openstack-CAL-Test: Test Create Virtual Link API")
852 vlink_req = self._get_virtual_link_request_info()
853
854 rc, rsp = self.cal.create_virtual_link(self._acct, vlink_req)
855 self.assertEqual(rc, RwStatus.SUCCESS)
856 logger.info("Openstack-CAL-Test: Created virtual_link with Id: %s" %rsp)
857 vlink_id = rsp
858
859 #Check if virtual_link create is successful
860 rc, rsp = self.cal.get_virtual_link(self._acct, rsp)
861 self.assertEqual(rc, RwStatus.SUCCESS)
862 self.assertEqual(rsp.virtual_link_id, vlink_id)
863
864 # Now create VDU
865 vdu_req = self._get_vdu_request_info(vlink_id)
866 logger.info("Openstack-CAL-Test: Test Create VDU API")
867
868 rc, rsp = self.cal.create_vdu(self._acct, vdu_req)
869 self.assertEqual(rc, RwStatus.SUCCESS)
870 logger.info("Openstack-CAL-Test: Created vdu with Id: %s" %rsp)
871
872 vdu_id = rsp
873
874 ## Check if VDU create is successful
875 rc, rsp = self.cal.get_vdu(self._acct, rsp)
876 self.assertEqual(rsp.vdu_id, vdu_id)
877
878 ### Wait until vdu_state is active
879 for i in range(50):
880 rc, rs = self.cal.get_vdu(self._acct, vdu_id)
881 self.assertEqual(rc, RwStatus.SUCCESS)
882 logger.info("Openstack-CAL-Test: VDU with id : %s. Reached State : %s" %(vdu_id, rs.state))
883 if rs.state == 'active':
884 break
885 rc, rs = self.cal.get_vdu(self._acct, vdu_id)
886 self.assertEqual(rc, RwStatus.SUCCESS)
887 self.assertEqual(rs.state, 'active')
888 logger.info("Openstack-CAL-Test: VDU with id : %s reached expected state : %s" %(vdu_id, rs.state))
889 logger.info("Openstack-CAL-Test: VDUInfo: %s" %(rs))
890
891 vlink_req = self._get_virtual_link_request_info()
892
893 ### Create another virtual_link
894 rc, rsp = self.cal.create_virtual_link(self._acct, vlink_req)
895 self.assertEqual(rc, RwStatus.SUCCESS)
896 logger.info("Openstack-CAL-Test: Created virtual_link with Id: %s" %rsp)
897 vlink_id2= rsp
898
899 ### Now exercise the modify_vdu_api
900 vdu_modify = self._get_vdu_modify_request_info(vdu_id, vlink_id2)
901 rc = self.cal.modify_vdu(self._acct, vdu_modify)
902 self.assertEqual(rc, RwStatus.SUCCESS)
903 logger.info("Openstack-CAL-Test: Modified vdu with Id: %s" %vdu_id)
904
905 ### Lets delete the VDU
906 self.cal.delete_vdu(self._acct, vdu_id)
907
908 ### Lets delete the Virtual Link
909 self.cal.delete_virtual_link(self._acct, vlink_id)
910
911 ### Lets delete the Virtual Link-2
912 self.cal.delete_virtual_link(self._acct, vlink_id2)
913
914 time.sleep(5)
915 ### Verify that VDU and virtual link are successfully deleted
916 rc, rsp = self.cal.get_vdu_list(self._acct)
917 self.assertEqual(rc, RwStatus.SUCCESS)
918 for vdu in rsp.vdu_info_list:
919 self.assertNotEqual(vdu.vdu_id, vdu_id)
920
921 rc, rsp = self.cal.get_virtual_link_list(self._acct)
922 self.assertEqual(rc, RwStatus.SUCCESS)
923
924 for virtual_link in rsp.virtual_link_info_list:
925 self.assertNotEqual(virtual_link.virtual_link_id, vlink_id)
926
927 logger.info("Openstack-CAL-Test: VDU/Virtual Link create-delete test successfully completed")
928
929
930 class VmData(object):
931 """A convenience class that provides all the stats and EPA Attributes
932 from the VM provided
933 """
934 def __init__(self, host, mgmt_ip):
935 """
936 Args:
937 host (str): host name.
938 mgmt_ip (str): The IP of the newly created VM.
939 """
940 # Sleep for 20s to ensure the VM is UP and ready to run commands
941 time.sleep(20)
942 logger.info("Connecting to host: {} and IP: {}".format(host, mgmt_ip))
943 self.client = paramiko.SSHClient()
944 self.client.set_missing_host_key_policy(paramiko.WarningPolicy())
945 self.client.connect(host)
946 self.ip = mgmt_ip
947
948 # Get all data from the newly created VM.
949 self._data = self._get_data()
950 self._page_size = self._exec_and_clean("getconf PAGE_SIZE")
951 self._disk_space = self._exec_and_clean(
952 "df -kh --output=size /",
953 line_no=1)
954 self._pci_data = self._exec('lspci -m | grep "10-Gigabit"')
955
956 def _get_data(self,):
957 """Runs the command and store the output in a python dict.
958
959 Returns:
960 dict: Containing all key => value pairs.
961 """
962 content = {}
963 cmds = ["lscpu", 'less /proc/meminfo']
964 for cmd in cmds:
965 ssh_out = self._exec(cmd)
966 content.update(self._convert_to_dict(ssh_out))
967 return content
968
969 def _exec_and_clean(self, cmd, line_no=0):
970 """A convenience method to run a command and extract the specified line
971 number.
972
973 Args:
974 cmd (str): Command to execute
975 line_no (int, optional): Default to 0, extracts the first line.
976
977 Returns:
978 str: line_no of the output of the command.
979 """
980 output = self._exec(cmd)[line_no]
981 output = ' '.join(output.split())
982 return output.strip()
983
984 def _exec(self, cmd):
985 """Thin wrapper that runs the command and returns the stdout data
986
987 Args:
988 cmd (str): Command to execute.
989
990 Returns:
991 list: Contains the command output.
992 """
993 _, ssh_out, _ = self.client.exec_command(
994 "/usr/rift/bin/ssh_root {} {}".format(self.ip,
995 cmd))
996 return ssh_out.readlines()
997
998 def _convert_to_dict(self, content):
999 """convenience method that cleans and stores the line into dict.
1000 data is split based on ":" or " ".
1001
1002 Args:
1003 content (list): A list containing the stdout.
1004
1005 Returns:
1006 dict: containing stat attribute => value.
1007 """
1008 flattened = {}
1009 for line in content:
1010 line = ' '.join(line.split())
1011 if ":" in line:
1012 key, value = line.split(":")
1013 else:
1014 key, value = line.split(" ")
1015 key, value = key.strip(), value.strip()
1016 flattened[key] = value
1017 return flattened
1018
1019 @property
1020 def disk(self):
1021 disk = self._disk_space.replace("G", "")
1022 return int(disk)
1023
1024 @property
1025 def numa_node_count(self):
1026 numa_cores = self._data['NUMA node(s)']
1027 numa_cores = int(numa_cores)
1028 return numa_cores
1029
1030 @property
1031 def vcpus(self):
1032 cores = int(self._data['CPU(s)'])
1033 return cores
1034
1035 @property
1036 def cpu_threads(self):
1037 threads = int(self._data['Thread(s) per core'])
1038 return threads
1039
1040 @property
1041 def memory(self):
1042 memory = self._data['MemTotal']
1043 memory = int(memory.replace("kB", ""))/1000/1000
1044 return int(memory)
1045
1046 @property
1047 def memory_page_size(self):
1048 return self._page_size
1049
1050 @property
1051 def pci_passthrough_device_list(self):
1052 return self._pci_data
1053
1054
1055 if __name__ == "__main__":
1056 logging.basicConfig(level=logging.INFO)
1057 unittest.main()